Read the book: «Routineaufgaben mit Python automatisieren»
| Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+: www.dpunkt.plus |
Al Sweigart
Routineaufgaben mit Python automatisieren
Praktische Programmierlösungen für Einsteiger
2., aktualisierte und erweiterte Auflage

Al Sweigart
Lektorat: Dr. Michael Barabas
Lektoratsassitenz: Anja Weimer
Fachgutachter/in: Ari Lacenski und Philip James
Übersetzung & Satz: G&U Language & Publishing Services GmbH, www.gundu.com
Copy-Editing: Ursula Zimpfer, Herrenberg
Herstellung: Stefanie Weidner
Umschlaggestaltung: Helmut Kraus, www.exclam.de
nach der Originalvorlage von No Starch Press
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN:
Print 978-3-86490-753-1
PDF 978-3-96088-956-4
ePub 978-3-96088-957-1
mobi 978-3-96088-958-8
2., aktualisierte und erweiterte Auflage 2020
Copyright © 2020 dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
Copyright © 2020 by Al Sweigart. Title of English-language original: Automate the Boring Stuff with Python, 2th Edition: Practical Programming for Total Beginners, ISBN 978-1-59327-992-9, published by No Starch Press. German-language edition copyright © 2020 by dpunkt.verlag. All rights reserved.
Hinweis:
Dieses Buch wurde auf PEFC-zertifiziertem Papier aus nachhaltiger Waldwirtschaft gedruckt. Der Umwelt zuliebe verzichten wir zusätzlich auf die Einschweißfolie.

Schreiben Sie uns:
Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: hallo@dpunkt.de.
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag noch Übersetzer können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
5 4 3 2 1 0
Inhalt
Der Autor
Der Fachgutachter
Danksagung
Einleitung
Für wen ist dieses Buch gedacht?
Programmierstil
Was ist Programmierung?
Was ist Python?
Programmierer müssen nicht viel Mathe können
Sie sind nie zu alt, um programmieren zu lernen
Programmierung ist kreativ
Der Aufbau dieses Buchs
Python herunterladen und installieren
Mu herunterladen und installieren
Mu starten
IDLE starten
Die interaktive Shell
Drittanbietermodule installieren
Hilfe finden
Sinnvolle Fragen stellen
Zusammenfassung
Teil 1Grundlagen der Python-Programmierung
1Grundlagen von Python
Ausdrücke in die interaktive Shell eingeben
Die Datentypen für ganze Zahlen, Fließkommazahlen und Strings
Stringverkettung und -wiederholung
Werte in Variablen speichern
Zuweisungsanweisungen
Variablennamen
Ihr erstes Programm
Analyse des Programms
Kommentare
Die Funktion print()
Die Funktion input()
Den Benutzernamen ausgeben
Die Funktion len()
Die Funktionen str(), int() und float()
Zusammenfassung
Wiederholungsfragen
2Flusssteuerung
Boolesche Werte
Vergleichsoperatoren
Boolesche Operatoren
Binäre boolesche Operatoren
Der Operator not
Kombinierte Verwendung von booleschen und Vergleichsoperatoren
Elemente zur Flusssteuerung
Bedingungen
Codeblöcke
Programmausführung
Flusssteuerungsanweisungen
If-Anweisungen
Else-Anweisungen
Elif-Anweisungen
While-Schleifen
Break-Anweisungen
Continue-Anweisungen
For-Schleifen und die Funktion range()
Module importieren
From-import-Anweisungen
Programme mit sys.exit() vorzeitig beenden
Ein kurzes Programm: Zahlen raten
Ein kurzes Programm: Stein, Schere, Papier
Zusammenfassung
Wiederholungsfragen
3Funktionen
Def-Anweisungen mit Parametern
Terminologie
Rückgabewerte und die Anweisung return
Der Wert None
Schlüsselwortargumente und print()
Der Aufrufstack
Lokaler und globaler Gültigkeitsbereich
Lokale Variablen können im globalen Gültigkeitsbereich nicht verwendet werden
Lokale Gültigkeitsbereiche können keine Variablen aus anderen lokalen Gültigkeitsbereichen verwenden
Globale Variablen können von einem lokalen Gültigkeitsbereich aus gelesen werden
Lokale und globale Variablen mit demselben Namen
Die Anweisung global
Ausnahmebehandlung
Ein kurzes Programm: Zickzackmuster
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Die Collatz-Folge
Eingabevalidierung
4Listen
Der Datentyp für Listen
Einzelne Elemente einer Liste mithilfe des Index abrufen
Negative Indizes
Teillisten mithilfe von Slices abrufen
Die Länge einer Liste mit len() abrufen
Werte in einer Liste mithilfe des Index ändern
Listenverkettung und -wiederholung
Elemente mit del aus einer Liste entfernen
Listen verwenden
Listen in for-Schleifen
Die Operatoren in und not in
Der Trick mit der Mehrfachzuweisung
Die Funktion enumerate() für Listen
Die Funktionen random.choice() und random.shuffle() für Listen
Erweiterte Zuweisungsoperatoren
Methoden
Elemente in einer Liste mit der Methode index() finden
Elemente mit den Methoden append() und insert() zu Listen hinzufügen
Elemente mit remove() aus Listen entfernen
Elemente in einer Liste mit sort() sortieren
Reihenfolge der Listeneinträge mit reverse() umkehren
Beispielprogramm: Magic 8 Ball mithilfe einer Liste
Sequenzielle Datentypen
Veränderbare und unveränderbare Datentypen
Der Datentyp für Tupel
Typen mit den Funktionen list() und tuple() umwandeln
Verweise
Identität und die Funktion id()
Verweise übergeben
Die Funktionen copy() und deepcopy() des Moduls copy
Ein kurzes Programm: Conways Spiel des Lebens
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Kommacode
Münzwurffolgen
Zeichenbildraster
5Dictionaries und Datenstrukturen
Der Datentyp für Dictionaries
Dictionaries und Listen im Vergleich
Die Methoden keys(), values() und items()
Das Vorhandensein eines Schlüssels oder Wertes im Dictionary ermitteln
Die Methode get()
Die Methode setdefault()
Saubere Ausgabe
Datenstrukturen zur Modellierung realer Objekte
Ein Tic-Tac-Toe-Brett
Verschachtelte Dictionaries und Listen
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Validierer für Schach-Dictionary
Inventar für ein Fantasyspiel
Eine Funktion zum Hinzufügen von Listeninhalten zum Inventar-Dictionary
6Stringbearbeitung
Umgang mit Strings
Stringliterale
Strings indizieren und Slices entnehmen
Die Operatoren in und not in für Strings
Strings in andere Strings einfügen
Nützliche Stringmethoden
Die Stringmethoden upper(), lower(), isupper() und islower()
Die isX-Stringmethoden
Die Stringmethoden startswith() und endswith()
Die Methoden join() und split()
Strings mit der Methode partition() aufteilen
Text mit rjust(), ljust() und center() ausrichten
Weißraum mit strip(), rstrip() und lstrip() entfernen
Die Funktionen ord() und chr() für numerische Zeichenwerte
Strings mit dem Modul pyperclip kopieren und einfügen
Projekt: Automatisierte Nachrichten mithilfe einer Mehrfach-Zwischenablage
Schritt 1: Programmdesign und Datenstrukturen
Schritt 2: Befehlszeilenargumente verarbeiten
Schritt 3: Die richtige Antwort kopieren
Projekt: Aufzählungspunkte zu einem Wiki-Markup hinzufügen
Schritt 1: Text von und zur Zwischenablage übertragen
Schritt 2: Textzeilen trennen und Sternchen hinzufügen
Schritt 3: Die veränderten Zeilen zusammenfügen
Ein kurzes Programm: Pig Latin
Zusammenfassung
Wiederholungsfragen
Übungsprojekt
Tabellenausgabe
Bots für Zombie Dice
Teil 2Aufgaben automatisieren
7Mustervergleich mit regulären Ausdrücken
Textmuster ohne reguläre Ausdrücke finden
Textmuster mithilfe regulärer Ausdrücke finden
Regex-Objekte erstellen
Vergleiche mit einem Regex-Objekt
Zusammenfassung: Mustervergleich mit regulären Ausdrücken
Weitere Möglichkeiten für den Mustervergleich mithilfe regulärer Ausdrücke
Gruppierung durch Klammern
Mithilfe der Pipe nach Übereinstimmungen mit mehreren Gruppen suchen
Optionale Übereinstimmung mit dem Fragezeichen
Mit dem Sternchen nach null oder mehr Übereinstimmungen suchen
Mit dem Pluszeichen nach einer oder mehr Übereinstimmungen suchen
Mit geschweiften Klammern nach einer genauen Zahl von Wiederholungen suchen
Gieriger und nicht gieriger Mustervergleich
Die Methode findall()
Zeichenklassen
Eigene Zeichenklassen bilden
Zirkumflex und Dollarzeichen
Das Jokerzeichen
Beliebige Übereinstimmungen mit Punkt-Stern finden
Zeilenumbrüche mit dem Punktsymbol finden
Übersicht über Regex-Symbole
Übereinstimmungen ohne Berücksichtigung der Groß- und Kleinschreibung
Strings mit der Methode sub() ersetzen
Umgang mit komplizierten regulären Ausdrücken
Die Variablen re.IGNORECASE, re.DOTALL und re.VERBOSE kombinieren
Projekt: Extraktionsprogramm für Telefonnummern und E-Mail-Adressen
Schritt 1: Einen regulären Ausdruck für Telefonnummern erstellen
Schritt 2: Einen regulären Ausdruck für E-Mail-Adressen erstellen
Schritt 3: Alle Überstimmungen im Inhalt der Zwischenablage finden
Schritt 4: Die gefundenen Übereinstimmungen zu einem String kombinieren
Das Programm ausführen
Ideen für ähnliche Programme
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Datumserkennung
Passwortstärke ermitteln
Regex-Version von strip()
8Eingabevalidierung
Das Modul PyInputPlus
Die Schlüsselwortargumente min, max, greaterThan und lessThan
Das Schlüsselwortargument blank
Die Schlüsselwortargumente limit, timeout und default
Die Schlüsselwortargumente allowRegexes und blockRegexes
Eine eigene Validierungsfunktion an inputCustom() übergeben
Projekt: Einen Trottel stundenlang beschäftigen
Projekt: Multiplikationstest
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Sandwichzubereiter
Ein eigenes Programm für den Multiplikationstest
9Dateien lesen und schreiben
Dateien und Dateipfade
Backslash unter Windows und Schrägstrich unter macOS und Linux
Pfade mit dem Operator / zusammenfügen
Das aktuelle Arbeitsverzeichnis
Das Benutzerverzeichnis
Absolute und relative Pfade
Neue Ordner mit os.makedirs() erstellen
Absolute und relative Pfade verwenden
Die Komponenten eines Dateipfads abrufen
Dateigrößen und Ordnerinhalte ermitteln
Eine Dateiliste mit Glob-Mustern bearbeiten
Die Gültigkeit von Pfaden prüfen
Dateien lesen und schreiben
Dateien mit der Funktion open() öffnen
Die Inhalte einer Datei lesen
Dateien schreiben
Variablen mit dem Modul shelve speichern
Variablen mit der Funktion pprint.pformat() speichern
Projekt: Zufallsgenerator für Tests
Schritt 1: Die Daten für den Test in einem Dictionary speichern
Schritt 2: Die Fragebogendatei erstellen und die Fragen mischen
Schritt 3: Die Auswahl der möglichen Antworten zusammenstellen
Schritt 4: Den Inhalt der Dateien für die Frage- und Lösungsbogen schreiben
Projekt: Aktualisierbare Mehrfach-Zwischenablage
Schritt 1: Kommentare und Vorbereitungen für die Shelf-Daten
Schritt 2: Den Inhalt der Zwischenablage unter einem Schlüsselwort speichern
Schritt 3: Schlüsselwörter auflisten und Inhalte laden
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Erweiterte Mehrfach-Zwischenablage
Lückentextspiel
Regex-Suche
10Dateien verwalten
Das Modul shutil
Dateien und Ordner kopieren
Dateien und Ordner verschieben und umbenennen
Dateien und Ordner unwiederbringlich löschen
Sicheres Löschen mit dem Modul send2trash
Einen Verzeichnisbaum durchlaufen
Dateien mit dem Modul zipfile komprimieren
ZIP-Dateien lesen
ZIP-Dateien entpacken
ZIP-Dateien erstellen und Inhalte hinzufügen
Projekt: Amerikanische Datumsangaben in Dateinamen in europäische Datumsangaben ändern
Projekt: Amerikanische Datumsangaben in Dateinamen in …
Schritt 1: Einen regulären Ausdruck für amerikanische Datumsangaben definieren
Schritt 2: Die einzelnen Teile der Datumsangabe in den Dateinamen ermitteln
Schritt 3: Die neuen Dateinamen zusammenstellen und die Dateien umbenennen
Vorschläge für ähnliche Programme
Projekt: Einen Ordner in einer ZIP-Datei sichern
Schritt 1: Den Namen der ZIP-Datei bestimmen
Schritt 2: Die neue ZIP-Datei erstellen
Schritt 3: Den Verzeichnisbaum durchlaufen und Inhalte zur ZIP-Datei hinzufügen
Vorschläge für ähnliche Programme
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Selektives Kopieren
Nicht mehr benötigte Dateien löschen
Lücken entfernen
11Debugging
Ausnahmen auslösen
Traceback als String abrufen
Zusicherungen (Assertions)
Zusicherungen in einem Ampelsimulator
Protokollierung
Das Modul logging verwenden
Kein Debugging mit print()
Protokolliergrade
Die Protokollierung deaktivieren
Protokollierung in eine Datei
Der Debugger von Mu
Continue
Step In
Step Over
Step Out
Stop
Debugging eines Additionsprogramms
Haltepunkte
Zusammenfassung
Wiederholungsfragen
Übungsprojekt
Münzwurfprogramm
12Web Scraping
Projekt: mapIt.py mit dem Modul webbrowser
Schritt 1: Die URL herausfinden
Schritt 2: Befehlszeilenargumente verarbeiten
Schritt 3: Den Inhalt der Zwischenablage verarbeiten und den Browser starten
Vorschläge für ähnliche Programme
Dateien mithilfe des Moduls requests aus dem Web herunterladen
Eine Webseite mit der Funktion requests.get() herunterladen
Auf Fehler prüfen
Heruntergeladene Dateien auf der Festplatte speichern
HTML
Quellen zu HTML
Ein kleiner Auffrischungskurs
Den HTML-Quellcode einer Webseite einsehen
Die Entwicklertools des Browsers öffnen
HTML-Elemente mithilfe der Entwicklertools finden
HTML mit dem Modul bs4 durchsuchen
Ein BeautifulSoup-Objekt aus dem HTML-Text erstellen
Elemente mit der Methode select() finden
Daten aus den Attributen eines Elements abrufen
Projekt: Alle Suchergebnisse öffnen
Schritt 1: Die Befehlszeilenargumente abrufen und die Suchergebnisseite anfordern
Schritt 2: Alle Ergebnisse finden
Schritt 3: Browsertabs für jedes Suchergebnis öffnen
Vorschläge für ähnliche Programme
Projekt: Alle XKCD-Comics herunterladen
Schritt 1: Den Aufbau des Programms festlegen
Schritt 2: Die Webseite herunterladen
Schritt 3: Das Bild des Comics finden und herunterladen
Schritt 4: Das Bild speichern und den vorherigen Comic suchen
Vorschläge für ähnliche Programme
Den Browser mit dem Modul selenium steuern
Einen seleniumgesteuerten Browser starten
Elemente auf der Seite finden
Auf Elemente klicken
Formulare ausfüllen und absenden
Die Betätigung von Sondertasten simulieren
Auf Browserschaltflächen klicken
Weitere Informationen über Selenium
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
E-Mail-Programm für die Befehlszeile
Downloadprogramm für Fotowebsites
2048
Linküberprüfung
13Excel-Arbeitsblätter
Excel-Dokumente
Das Modul openpyxl installieren
Excel-Dokumente lesen
Excel-Dokumente mit OpenPyXL öffnen
Arbeitsblätter aus der Arbeitsmappe abrufen
Zellen eines Arbeitsblatts abrufen
Umrechnen zwischen Kennbuchstaben und Nummern
Zeilen und Spalten eines Arbeitsblatts abrufen
Arbeitsmappen, Arbeitsblätter und Zellen
Projekt: Daten in einer Arbeitsmappe lesen
Schritt 1: Die Daten der Arbeitsmappe lesen
Schritt 2: Die Datenstruktur füllen
Schritt 3: Die Ergebnisse in eine Datei schreiben
Vorschläge für ähnliche Programme
Excel-Dokumente schreiben
Excel-Dokumente erstellen und speichern
Arbeitsblätter erstellen und entfernen
Werte in Zellen schreiben
Projekt: Ein Arbeitsblatt aktualisieren
Schritt 1: Eine Datenstruktur mit den neuen Informationen einrichten
Schritt 2: Alle Zeilen prüfen und die falschen Preise korrigieren
Vorschläge für ähnliche Programme
Die Schrift in den Zellen gestalten
Font-Objekte
Formeln
Das Erscheinungsbild von Zeilen und Spalten festlegen
Zeilenhöhe und Spaltenbreite festlegen
Zellen verbinden und aufteilen
Bereiche fixieren
Diagramme
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Multiplikationstabellen erstellen
Leere Zeilen einfügen
Zellen transponieren
Textdateien in Arbeitsblätter umwandeln
Arbeitsblätter in Textdateien umwandeln
14Google Tabellen
EZSheets installieren und einrichten
Anmeldeinformationen und Tokendateien beziehen
Die Datei mit den Anmeldeinformationen widerrufen
Spreadsheet-Objekte
Tabellen erstellen, hochladen und auflisten
Tabellenattribute
Tabellen hoch- und herunterladen
Tabellen löschen
Sheet-Objekte
Daten lesen und schreiben
Tabellenblätter erstellen und löschen
Tabellenblätter kopieren
Grenzwerte für Google Tabellen
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Daten von Google Formulare herunterladen
Tabellen in andere Formate umwandeln
Fehler in einer Tabelle finden
15PDF- und Word-Dokumente
PDF-Dokumente
Text aus PDFs entnehmen
PDFs entschlüsseln
PDFs erstellen
Projekt: Ausgewählte Seiten aus mehreren PDFs kombinieren
Schritt 1: Alle PDF-Dateien finden
Schritt 2: Die einzelnen PDFs öffnen
Schritt 3: Die einzelnen Seiten hinzufügen
Schritt 4: Die Ergebnisse speichern
Vorschläge für ähnliche Programme
Word-Dokumente
Word-Dokumente lesen
Den kompletten Text einer .docx-Datei abrufen
Absätze und Run-Objekte formatieren
Word-Dokumente mit anderen als den Standardformaten erstellen
Run-Attribute
Word-Dokumente schreiben
Überschriften hinzufügen
Zeilenwechsel und Seitenumbrüche hinzufügen
Bilder einfügen
PDF-Dateien aus Word-Dokumenten erstellen
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
PDF-Paranoia
Personalisierte Einladungen als Word-Dokument
Brute-Force-Passwortknacker für PDFs
16CSV-Dateien und JSON-Daten
Das Modul csv
Reader-Objekte
Daten in einer for-Schleife aus reader-Objekten lesen
Writer-Objekte
Die Schlüsselwortargumente delimiter und lineterminator
DictReader- und DictWriter-Objekte
Projekt: Kopfzeilen aus CSV-Dateien entfernen
Schritt 1: Alle CSV-Dateien durchlaufen
Schritt 2: Die CSV-Datei lesen
Schritt 3: Die CSV-Datei ohne die erste Zeile schreiben
Vorschläge für ähnliche Programme
JSON und APIs
Das Modul json
JSON-Daten mit der Funktion loads() laden
JSON-Daten mit der Funktion dumps() schreiben
Projekt: Die aktuellen Wetterdaten abrufen
Schritt 1: Den Standort aus dem Befehlszeilenargument entnehmen
Schritt 2: Die JSON-Daten herunterladen
Schritt 3: JSON-Daten laden und die Wettervorhersage ausgeben
Vorschläge für ähnliche Programme
Zusammenfassung
Wiederholungsfragen
Übungsprojekt
Excel-in-CSV-Konverter
17Zeit und Aufgabenplanung
Das Modul time
Die Funktion time.time()
Die Funktion time.sleep()
Zahlen runden
Projekt: Superstoppuhr
Schritt 1: Das Programm auf die Zeitmessung vorbereiten
Schritt 2: Intervalldauern messen und anzeigen
Vorschläge für ähnliche Programme
Das Modul datetime
Der Datentyp timedelta
Anhalten bis zu einem bestimmten Zeitpunkt
datetime-Objekte in Strings umwandeln
Strings in datetime-Objekte umwandeln
Die Zeitfunktionen von Python im Überblick
Multithreading
Argumente an die Zielfunktion eines Threads übergeben
Probleme der Nebenläufigkeit
Projekt: Multithread-Version des XKCD-Downloadprogramms
Schritt 1: Eine Funktion für den Download verwenden
Schritt 2: Threads erstellen und starten
Schritt 3: Auf das Ende aller Threads warten
Andere Programme von Python aus starten
Befehlszeilenargumente an Popen() übergeben
Taskplaner, launchd und cron
Websites mit Python aufrufen
Andere Python-Skripte ausführen
Dateien in ihren Standardanwendungen öffnen
Projekt: Ein einfaches Countdown-Programm
Schritt 1: Der Countdown
Schritt 2: Die Klangdatei abspielen
Vorschläge für ähnliche Programme
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Elegantere Stoppuhr
Webcomic-Downloadprogramm mit Zeitplanung
18E-Mails und Textnachrichten
E-Mails mit der Gmail-API senden und empfangen
Die Gmail-API aktivieren
E-Mails von einem Gmail-Konto senden
E-Mails in einem Gmail-Konto lesen
E-Mails in einem Gmail-Konto suchen
Anhänge von einem Gmail-Konto herunterladen
SMTP
E-Mails senden
Verbindung mit einem SMTP-Server aufnehmen
Die »Hallo«-Nachricht an den SMTP-Server senden
Die TLS-Verschlüsselung einleiten
Am SMTP-Server anmelden
Eine E-Mail senden
Die Verbindung zum SMTP-Server trennen
IMAP
E-Mails mit IMAP abrufen und löschen
Verbindung mit einem IMAP-Server aufnehmen
Am IMAP-Server anmelden
Nach E-Mails suchen
E-Mails abrufen und als gelesen markieren
E-Mail-Adressen aus einer Rohnachricht gewinnen
Den Rumpf aus einer Rohnachricht gewinnen
E-Mails löschen
Die Verbindung zum IMAP-Server trennen
Projekt: E-Mails über ausstehende Mitgliedsbeiträge senden
Schritt 1: Die Excel-Datei öffnen
Schritt 2: Alle säumigen Mitglieder finden
Schritt 3: Personalisierte E-Mail-Mahnungen senden
Textnachrichten über SMS-E-Mail-Gateways senden
Textnachrichten mit Twilio senden
Ein Twilio-Konto einrichten
Textnachrichten senden
Projekt: Das Modul »Just Text Me«
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Zufällige Zuweisung von Arbeiten
Regenschirmhinweis
Automatischer Entregistrierer
Den Computer per E-Mail steuern
19Bildbearbeitung
Grundlagen zur Bilddarstellung auf Computern
Farben und RGBA-Werte
Koordinaten und Rechtecktupel
Bildbearbeitung mit Pillow
Mit dem Datentyp Image arbeiten
Bilder beschneiden
Bilder kopieren und in andere Bilder einfügen
Die Bildgröße ändern
Bilder drehen und spiegeln
Einzelne Pixel ändern
Projekt: Ein Logo hinzufügen
Schritt 1: Das Logobild öffnen
Schritt 2: Alle Dateien durchlaufen und die Bilder öffnen
Schritt 3: Die Bildgröße ändern
Schritt 4: Logo hinzufügen und Änderungen speichern
Vorschläge für ähnliche Programme
Bilder zeichnen
Formen zeichnen
Text zeichnen
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Das Logoprogramm erweitern und verbessern
Fotoordner auf der Festplatte finden
Personalisierte Tischkarten
20GUI-Automatisierung
Das Modul PyAutoGUI installieren
Zugriff auf macOS erlauben
Kleine Probleme beheben
Die PyAutoGUI-Notfallsicherung
Beenden durch Abmelden
Den Mauszeiger steuern
Den Mauszeiger bewegen
Die Position des Mauszeigers abrufen
Mausinteraktionen
Klicken
Ziehen
Scrollen
Mausbewegungen planen
Auf dem Bildschirm arbeiten
Einen Screenshot aufnehmen
Einen Screenshot analysieren
Bilderkennung
Informationen über das Fenster abrufen
Das aktive Fenster abrufen
Andere Möglichkeiten zum Abrufen von Fenstern
Fenster bearbeiten
Die Tastatur steuern
Strings von der Tastatur senden
Tastennamen
Tasten drücken und loslassen
Tastenkombinationen
GUI-Automatisierungsskripte einrichten
Übersicht über die Funktionen von PyAutoGUI
Projekt: Formulare automatisch ausfüllen
Schritt 1: Den Ablauf herausfinden
Schritt 2: Die Koordinaten ermitteln
Schritt 3: Daten eingeben
Schritt 4: Auswahllisten und Optionsschalter
Schritt 5: Das Formular absenden und warten
Dialogfelder anzeigen
Zusammenfassung
Wiederholungsfragen
Übungsprojekte
Beschäftigung vortäuschen
Textfelder über Zwischenablage lesen
Instant-Messenger-Bot
Tutorial für einen Spiele-Bot
Anhang
A Drittanbietermodule installieren
Pip
Drittanbietermodule installieren
Module für Mu installieren
B Programme ausführen
Programme im Terminalfenster ausführen
Python-Programme unter Windows ausführen
Python-Programme unter macOS ausführen
Programme unter Ubuntu Linux ausführen
Python-Programme mit ausgeschalteten Zusicherungen ausführen
C Antworten auf die Wiederholungsfragen
Kapitel 1
Kapitel 2
Kapitel 3
Kapitel 4
Kapitel 5
Kapitel 6
Kapitel 7
Kapitel 8
Kapitel 9
Kapitel 10
Kapitel 11
Kapitel 12
Kapitel 13
Kapitel 14
Kapitel 15
Kapitel 16
Kapitel 17
Kapitel 18
Kapitel 19
Kapitel 20
Stichwortverzeichnis
Für meinen Neffen Jack
Der Autor
Al Sweigart ist Softwareentwickler und Fachbuchautor. Seine Lieblingsprogrammiersprache ist Python und er hat bereits mehrere Open-Source-Module dafür entwickelt. Seine anderen Bücher sind auf seiner Website http://www.inventwithpython.com/ unter einer Creative-Commons-Lizenz kostenlos erhältlich. Seine Katze wiegt 11 Pfund.
Der Fachgutachter
Philip James arbeitet schon mehr als zehn Jahre mit Python und tritt in der Python-Community häufig als Redner zu unterschiedlichen Themen von Unix-Grundlagen bis zu Social Media auf Open-Source-Basis auf. Er ist einer der Hauptbeteiligten am BeeWare-Projekt und lebt mit seiner Partnerin Nic und deren Katze River in der Bay Area von San Francisco.
Danksagung
Es ist irreführend, dass nur mein Name auf dem Umschlag steht, denn um dieses Buch schreiben zu können, habe ich die Hilfe vieler Menschen benötigt. Ich möchte meinem Herausgeber Bill Pollock, meinen Lektoren Laurel Chun, Leslie Shen, Greg Poulos, Jennifer Griffith-Delgado und Frances Saux sowie allen anderen Mitarbeitern bei No Starch Press für ihre unschätzbare Hilfe danken. Ein Dank geht auch an meine Fachgutachter Ari Lacenski und Philip James für ihre hervorragenden Vorschläge und Verbesserungen sowie für ihre Unterstützung.
Vielen Dank auch allen Mitgliedern der Python Software Foundation für ihre großartige Arbeit. Die Python-Community ist die beste, die ich in der IT-Branche gefunden habe.
Schließlich möchte ich noch meiner Familie, meinen Freunden und der Clique bei Shotwell’s danken, weil sie es mir nicht übel genommen haben, dass ich während des Schreibens an diesem Buch dauernd beschäftigt war. Dankeschön!
Einleitung
»Du hast gerade in zwei Stunden das erledigt, woran wir drei sonst zwei Tage lang sitzen!«
Mein Mitbewohner in den frühen 2000er-Jahren arbeitete bei einem Elektronikhändler, bei dem gelegentlich eine Tabelle mit Tausenden von Produktpreisen anderer Läden auftauchte. Drei Mitarbeiter druckten diese Tabelle dann auf einem dicken Stapel Papier aus und teilten diesen unter sich auf. Für jeden Produktpreis schlugen sie den Preis ihres eigenen Arbeitgebers nach und notierten alle Produkte, die die Konkurrenz billiger anbot. Damit waren sie gewöhnlich zwei Tage beschäftigt.
»Wenn ich die Originaldatei bekomme, kann ich ein Programm schreiben, das die Arbeit für euch erledigt«, schlug mein Mitbewohner ihnen vor, als er sah, wie sie inmitten eines Riesenhaufens Papier auf dem Fußboden hockten.
Nach ein paar Stunden hatte er ein kurzes Programm geschrieben, das die Preisliste der Konkurrenten aus der Datei auslas, die Produkte in der Datenbank des Elektronikladens nachschlug und einen Vermerk machte, wenn die Konkurrenz billiger war. Er war immer noch ein Anfänger in Sachen Programmierung und hatte den Großteil dieser Stunden damit zugebracht, die Dokumentation in einem Programmierbuch nachzuschlagen. Die Ausführung des fertigen Programms dauerte nur wenige Sekunden. An dem Tag gönnten sich mein Mitbewohner und seine Kollegen eine besonders lange Mittagspause.
Das zeigt das Potenzial der Programmierung. Ein Computer ist wie ein Schweizer Messer und lässt sich für zahllose Aufgaben einrichten. Viele Leute bringen Stunden mit Klicken und Tippen zu, um monotone Aufgaben auszuführen, ohne zu ahnen, dass der Computer diese Arbeit in wenigen Sekunden erledigen könnte, wenn er nur die richtigen Anweisungen dafür bekäme.
Für wen ist dieses Buch gedacht?
Software bildet die Grundlage vieler unserer Geräte, die wir für die Arbeit und in der Freizeit verwenden: Fast jeder nutzt soziale Netzwerke zur Kommunikation, die Telefone vieler Menschen enthalten Computer mit Internetzugriff und für die meisten Büroarbeiten ist Computerarbeit erforderlich. Daher ist die Nachfrage nach Personen, die programmieren können, sprunghaft angestiegen. Unzählige Bücher, interaktive Webtutorials und Schulungen für Entwickler werden mit dem Versprechen beworben, ehrgeizige Anfänger zu Softwareingenieuren zu machen, die sechsstellige Gehälter verlangen können.
Dieses Buch ist jedoch nicht für diese Leute gedacht, sondern für alle anderen.
Mit diesem Buch allein können Sie nicht zu einem professionellen Softwareentwickler werden, genauso wenig, wie ein paar Gitarrenstunden Sie zu einem Rockstar machen. Wenn Sie aber Büroangestellter, Administrator oder Akademiker sind oder auch nur zur Arbeit oder zum Vergnügen einen Computer benutzen, so werden Sie hier die Grundlagen der Programmierung kennenlernen, um einfache Aufgaben wie die folgenden zu automatisieren:
Tausende von Dateien verschieben und umbenennen und in Ordner sortieren
Onlineformulare ausfüllen, ohne Text eingeben zu müssen
Dateien von einer Website herunterladen oder Texte von dort kopieren, sobald dort neues Material bereitgestellt wird
Sich von Ihrem Computer per SMS benachrichtigen lassen
Excel-Arbeitsblätter bearbeiten und formatieren
Nach neuen E-Mails suchen und vorformulierte Antworten senden
Diese Aufgaben sind einfach, aber zeitraubend, und sie sind häufig so trivial oder so spezifisch, dass es keine fertige Software dafür gibt. Mit einigen Programmierkenntnissen können Sie Ihren Computer diese Aufgaben für Sie erledigen lassen.
Programmierstil
Dieses Buch ist nicht als Nachschlagewerk gedacht, sondern als Anleitung für Anfänger. Der Programmierstil verstößt manchmal gegen die üblichen Richtlinien (beispielsweise werden in einigen Programmen globale Variablen verwendet), aber das ist ein Kompromiss, um das Lernen zu erleichtern. In diesem Buch lernen Sie, Wegwerfcode für einmalige Aufgaben zu schreiben, weshalb wir nicht viel Mühe auf Stil und Eleganz verwenden. Auch anspruchsvolle Programmierkonzepte wie Objektorientierung, Listenabstraktion und Generatoren werden hier aufgrund ihrer Kompliziertheit nicht behandelt. Altgediente Programmierer werden den Code sicherlich ändern wollen, um die Effizienz zu erhöhen, aber in diesem Buch geht es darum, Programme mit so wenig Aufwand wie möglich zum Laufen zu bekommen.
Was ist Programmierung?
In Filmen und Fernsehserien werden Programmierer oft als Leute dargestellt, die rasend schnell auf einer Tastatur herumtippen, um kryptische Folgen von Nullen und Einsen auf leuchtenden Bildschirmen erscheinen zu lassen. In Wirklichkeit ist moderne Programmierung aber nicht so geheimnisvoll. Programmierung ist einfach die Eingabe von Anweisungen, die der Computer ausführen soll. Diese Anweisungen können dazu dienen, mit Zahlen zu rechnen, Text zu ändern, Informationen in Dateien nachzuschlagen oder über das Internet mit anderen Computern zu kommunizieren.
Alle Programme bestehen aus einfachen Anweisungen, die die Grundbausteine darstellen. Einige der gebräuchlichsten dieser Anweisungen besagen, auf Deutsch übersetzt, Folgendes:
»Mach dies; dann mach das.«
»Wenn diese Bedingung wahr ist, dann führe diese Aktion aus; anderenfalls jene Aktion.«
»Mach dies genau 27 Mal.«
»Mach dies, solange die Bedingung wahr ist.«
Diese Bausteine können Sie kombinieren, um auch kompliziertere Entscheidungen zu treffen. Im folgenden Beispiel sehen Sie die Programmieranweisungen – den Quellcode – für ein einfaches Programm in der Programmiersprache Python. Die Software Python führt die einzelnen Codezeilen vom Anfang bis zum Ende aus. (Manche Zeilen werden nur ausgeführt, wenn (if) eine Bedingung wahr ist (true); anderenfalls (else) führt Python eine andere Zeile aus.)
passwordFile = open('SecretPasswordFile.txt') 
secretPassword = passwordFile.read() 
print('Enter your password.') 
typedPassword = input()
if typedPassword == secretPassword: 
print('Access granted') 
if typedPassword == '12345': 
print('That password is one that an idiot puts on their luggage.') 
else:
print('Access denied') 
Auch wenn Sie noch nicht viel von Programmierung verstehen, können Sie vielleicht schon erraten, was der vorstehende Code bewirkt. Als Erstes wird die Datei SecretPasswordFile.txt geöffnet (
) und das geheime Passwort gelesen (
). Danach wird der Benutzer aufgefordert, ein Passwort einzugeben (über die Tastatur) (
). Die beiden Passwörter werden verglichen (
), und wenn sie identisch sind, gibt das Programm auf dem Bildschirm die Meldung Access granted (»Zugriff gewährt«) aus (
). Danach prüft das Programm, ob das Passwort 12345 lautet (
). Wenn ja, gibt es dem Benutzer den dezenten Hinweis, dass dies nicht gerade die ideale Wahl für ein Passwort ist (
). Sind die Passwörter nicht identisch, gibt das Programm Access denied (»Zugriff verweigert«) aus (
).
Was ist Python?
Der Begriff Python bezeichnet die Programmiersprache Python (deren Syntaxregeln festlegen, was als gültiger Python-Code angesehen wird) und den Python-Interpreter, eine Software, die den (in der Sprache Python geschriebenen) Code liest und dessen Anweisungen ausführt. Den Python-Interpreter können Sie kostenlos von https://python.org/ herunterladen, wobei es Versionen für Linux, macOS und Windows gibt.
Der Name Python ist übrigens nicht von der Schlange abgeleitet, sondern von der surrealistischen britischen Komikergruppe Monty Python. Python-Programmierer werden liebevoll »Pythonistas« genannt, und Tutorials sowie die Dokumentation zu Python stecken voller Anspielungen sowohl auf Monty Python als auch auf Schlangen.
Programmierer müssen nicht viel Mathe können
Wenn mir jemand erklärt, warum er Angst davor hat, Programmieren zu lernen, geht es meistens darum, dass er glaubt, dazu müsste man sehr gut in Mathematik sein. In Wirklichkeit ist zur Programmierung meistens nicht mehr Mathe als einfache Grundrechenarten erforderlich. Programmieren lässt sich in diesem Punkt sogar mit dem Lösen von Sudoku-Rätseln vergleichen. Dazu müssen Sie in jede Zeile, jede Spalte und jedes innere 3x3-Quadrat des 9x9-Feldes die Zahlen von 1 bis 9 einfügen, wobei bereits einige Zahlen vorgegeben sind. Aus diesen Zahlen leiten Sie die Lösung durch Deduktion und Logik ab. In der Aufgabe in Abb. E–1 kommt beispielsweise eine 5 sowohl in der ersten als auch in der zweiten Zeile vor. Daher muss die 5 im oberen rechten Quadrat in der dritten Zeile stehen. Da sich auch schon eine 5 in der letzten Spalte befindet, kann die 5 in der dritten Zeile nicht rechts neben der 6 stehen. Damit bleibt nur der Platz links von der 6 übrig. Jede Zeile, jede Spalte und jedes Quadrat, das Sie lösen, gibt Ihnen weitere Hinweise für den Rest des Rätsels. Mit jeder Gruppe der Zahlen von 1 bis 9, die Sie vervollständigen, nähern Sie sich der Lösung des gesamten Rätsels.
Abb. 2–1 Ein Sudoku-Rätsel (links) und die Lösung (rechts). Beim Sudoku geht es zwar um Zahlen, doch ist dafür keine Mathematik erforderlich. (Bilder (c) Wikimedia Commons)
Nur weil es bei Sudoku um Zahlen geht, heißt das noch lange nicht, dass man gut in Mathe sein muss, um die Lösung auszuknobeln. Das Gleiche gilt auch fürs Programmieren. Wie beim Sudoku müssen Sie auch beim Programmieren das Problem in einzelne Schritte zerlegen. Beim Debuggen von Programmen (also beim Aufspüren und Beheben von Fehlern) müssen Sie geduldig beobachten, was das Programm macht, und die Ursachen von Fehlern herausfinden. Wie bei allen anderen Fähigkeiten werden Sie auch beim Programmieren umso besser, je mehr Erfahrung Sie haben.
Sie sind nie zu alt, um programmieren zu lernen
Den zweithäufigsten Einwand, den ich im Zusammenhang mit Programmierung zu hören bekomme, ist, dass jemand glaubt, zu alt zu sein, um diese Tätigkeit noch zu erlernen. Im Internet musste ich schon viele Kommentare von Leuten lesen, die meinten, sie seien mit sage und schreibe 23 Jahren (!) schon zu alt dafür. Das ist ganz sicher nicht »zu alt«, um programmieren zu lernen. Viele Menschen eignen sich in viel höherem Alter neue Fähigkeiten an.
Man muss nicht als Kind anfangen, um ein fähiger Programmierer zu werden. Die Vorstellung von Programmierern als Wunderkindern ist aber nicht totzukriegen. Leider habe ich selbst zu der Legende beigetragen, weil ich erzählt habe, dass ich bereits in der Grundschule mit dem Programmieren angefangen habe.
Programmieren lässt sich heutzutage leichter lernen als in den 90ern, denn heute gibt es viel mehr Bücher, bessere Suchmaschinen und viel mehr Websites, auf denen Sie Antworten auf Ihre Fragen bekommen. Vor allem aber sind die Programmiersprachen selbst viel benutzerfreundlicher geworden. Daher können Sie sich heute alles, was ich zwischen Grundschule und High-School-Abschluss über Programmierung gelernt habe, an ungefähr einem Dutzend Wochenenden erarbeiten. Mein Vorsprung war in Wirklichkeit gar kein so großer Vorsprung.
Programmieren lernt man nur durch Übung. Wir werden nicht als Programmierer geboren, und noch nicht programmieren zu können, heißt nicht, dass man niemals ein Experte auf diesem Gebiet werden könnte.
Programmierung ist kreativ
Programmieren ist eine ebenso kreative Tätigkeit wie Malen, Schreiben, Stricken und das Bauen mit Lego-Steinen. Genauso wie das Malen auf einer leeren Leinwand ist die Entwicklung von Software zwar gewissen Einschränkungen unterworfen, bietet aber auch unendlich viele Möglichkeiten.
Der Unterschied zwischen Programmierung und anderen kreativen Tätigkeiten besteht darin, dass Sie beim Programmieren das gesamte erforderliche Rohmaterial in Ihrem Computer haben. Sie müssen keine Leinwand, keine Farbe, keinen Film, kein Garn, keine Lego-Steine oder elektronischen Bauteile kaufen. Ein zehn Jahre alter Computer ist mehr als leistungsfähig genug, um damit Programme zu schreiben. Ein fertiges Programm können Sie beliebig oft kopieren. Während ein gestrickter Pullover immer nur von einer einzigen Person auf einmal getragen werden kann, lässt sich ein nützliches Programm auf einfache Weise der ganzen Welt online zur Verfügung stellen.
Der Aufbau dieses Buchs
Der erste Teil dieses Buchs behandelt die Grundlagen der Python-Programmierung. Im zweiten Teil sehen wir uns dann verschiedene Aufgaben an, die Sie automatisieren können. In jedem Kapitel des zweiten Teils gibt es Übungsprojekte. Die folgende Übersicht zeigt, was Sie in den einzelnen Kapiteln erwartet:
Teil I: Grundlagen der Python-Programmierung
Kapitel 1: Grundlagen von PythonHier werden Ausdrücke vorgestellt, die grundlegendste Art von Python-Anweisungen. Außerdem erfahren Sie, wie Sie die interaktive Shell von Python verwenden, um Code auszuprobieren.
Kapitel 2: FlusssteuerungIn diesem Kapitel erfahren Sie, wie Ihre Programme entscheiden können, welcher Code in einer bestimmten Situation ausgeführt werden soll. Dadurch können Sie auf unterschiedliche Bedingungen reagieren.
Kapitel 3: FunktionenDieses Kapitel zeigt Ihnen, wie Sie eigene Funktionen definieren, um Ihren Code in besser handhabbare Abschnitte zu gliedern.
Kapitel 4: ListenHier erhalten Sie eine Einführung in den Datentyp der Listen und erfahren, wie Sie damit Daten gliedern können.
Kapitel 5: Dictionaries und DatenstrukturenDieses Kapitel gibt eine Einführung in den Datentyp der Dictionaries und führt noch weitere Möglichkeiten auf, um Daten zu gliedern.
Kapitel 6: StringbearbeitungHier geht es um die Arbeit mit Textdaten (die in Python Strings genannt werden).
Teil II: Aufgaben automatisieren
Kapitel 7: Mustervergleich mit regulären AusdrückenHier erfahren Sie, wie Sie mit regulären Ausdrücken nach Textmustern suchen können.
Kapitel 8: EingabevalidierungHier wird erläutert, wie Ihr Programm die von Benutzern eingegebenen Informationen überprüfen kann, damit die Daten nicht in einer Form vorliegen, die im weiteren Verlauf des Programms Fehler verursachen kann.
Kapitel 9: Dateien lesen und schreibenDieses Kapitel erklärt, wie Ihre Programme den Inhalt von Textdateien lesen und selbst Informationen in Dateien auf der Festplatte speichern können.
Kapitel 10: Dateien verwaltenSie erfahren hier, wie Python große Mengen von Dateien kopieren, verschieben, umbenennen und löschen kann, und zwar viel schneller, als ein menschlicher Bearbeiter es tun könnte. Außerdem werden das Komprimieren und Entpacken von Dateien erklärt.
Kapitel 11: DebuggingHier werden die verschiedenen Instrumente vorgestellt, die in Python zur Verfügung stehen, um Fehler (Bugs) zu finden und zu beheben.
Kapitel 12: Web ScrapingDieses Kapitel zeigt Ihnen, wie Sie Programme schreiben, die automatisch Webseiten herunterladen und nach Informationen durchforsten. Dieser Vorgang wird Web Scraping genannt.
Kapitel 13: Excel-ArbeitsblätterHier geht es darum, wie Sie Excel-Arbeitsblätter programmgesteuert bearbeiten, sodass Sie das nicht manuell tun müssen. Das ist besonders praktisch, wenn die Anzahl der Dokumente, die Sie analysieren müssen, in die Hunderte oder gar in die Tausende geht.
Kapitel 14: Google TabellenIn diesem Kapitel erfahren Sie, wie Sie mit Python Daten in der häufig genutzten Online-Tabellenkalkulationsanwendung Google Tabellen lesen und bearbeiten.
Kapitel 15: PDF- und Word-DokumenteDieses Kapitel behandelt das programmgesteuerte Lesen von Word- und PDF-Dokumenten.
Kapitel 16: CSV-Dateien und JSON-DatenDie Erklärung der programmgesteuerten Bearbeitung von Dokumenten wird hier für CSV- und JSON-Dateien fortgesetzt.
Kapitel 17: Zeit und AufgabenplanungHier lernen Sie, wie Python Uhrzeiten und Kalenderdaten handhabt und wie Sie dafür sorgen, dass Ihr Computer Aufgaben zu einem bestimmten Zeitpunkt ausführt. Außerdem erfahren Sie, wie Sie von Python-Programmen aus andere Programme starten.
Kapitel 18: E-Mails und TextnachrichtenIn diesem Kapitel geht es darum, Programme zu schreiben, die an Ihrer Stelle E-Mails und Textnachrichten senden.
Kapitel 19: BildbearbeitungDieses Kapitel erklärt, wie Sie Bilder, z. B. JPEG- oder PNG-Dateien, in Ihren Programmen bearbeiten können.
Kapitel 20: GUI-AutomatisierungHier lernen Sie, wie Sie mit einem Programm die Maus und die Tastatur steuern, um Mausklicks und Tastenbetätigungen zu simulieren.
Anhang A: Drittanbietermodule installierenDieser Anhang zeigt, wie Sie Python mithilfe von nützlichen Modulen erweitern können.
Anhang B:Programme ausführenHier erfahren Sie, wie Sie Python-Programme unter Windows, macOS und Linux außerhalb des Codeeditors ausführen.
Anhang C:Antworten auf die WiederholungsfragenHier finden Sie die Lösungen sowie einige zusätzliche Erklärungen zu den Wiederholungsfragen, die am Ende jedes Kapitels stehen.
Python herunterladen und installieren
Python können Sie kostenlos für Windows, macOS und Linux von https://python.org/downloads/ herunterladen. Wenn Sie die neueste Version verwenden, die auf der Website angeboten wird, sollten alle Programme in diesem Buch funktionieren.
Warnung
Achten Sie darauf, eine Version von Python 3 herunterzuladen (z. B. 3.8.0). Die Programme in diesem Buch sind für Python 3 geschrieben. Auf Python 2 funktionieren sie unter Umständen gar nicht oder zumindest nicht korrekt.
Auf der Downloadseite finden Sie Installer für die verschiedenen Betriebssysteme und dabei wiederum jeweils für 64- und für 32-Bit-Computer. Als Erstes müssen Sie daher herausfinden, welchen Installer Sie brauchen. Wenn Sie Ihren Computer 2007 oder später gekauft haben, handelt es sich sehr wahrscheinlich um ein 64-Bit-System, anderenfalls eher um einen 32-Bit-Rechner. Genau herausfinden können Sie das wie folgt:
Auf Windows wählen Sie Start > Systemsteuerung > System und schauen nach, ob als Systemtyp 64 Bit oder 32 Bit angegeben wird.
Auf macOS wählen Sie im Apfelmenü Über diesen Mac > Weitere Informationen > Systembericht > Hardware. Schauen Sie sich in der Hardware-Übersicht den Eintrag unter Prozessortyp an. Wenn dort Intel Core Solo oder Intel Core Duo steht, haben Sie einen 32-Bit-Rechner. Bei allen anderen Einträgen (auch Intel Core 2 Duo) handelt es sich um einen 64-Bit-Computer.
Auf Ubuntu Linux geben Sie in einem Terminalfenster den Befehl uname -m ein. Die Antwort i686 bedeutet, dass Sie einen 32-Bit-Computer haben. Bei einem 64-Bit-Rechner lautet die Antwort x86_64.
Laden Sie auf Windows den Python-Installer (mit der Endung .msi) herunter und doppelklicken Sie darauf. Befolgen Sie die Anweisungen, die auf dem Bildschirm angezeigt werden. Der Vorgang läuft wie folgt ab:
1 Wählen Sie Install for All Users und dann Weiter.
2 Akzeptieren Sie in den nächsten Fenstern jeweils die Standardoptionen, indem Sie auf Weiter klicken.
Auf macOS laden Sie die passende .dmg-Datei für Ihre Version des Betriebssystems herunter und doppelklicken darauf. Befolgen Sie die Anweisungen, die auf dem Bildschirm angezeigt werden. Der Vorgang läuft wie folgt ab:
1 Wenn das DMG-Paket in einem neuen Fenster geöffnet wird, doppelklicken Sie auf die Datei Python.mpkg. Möglicherweise müssen Sie Ihr Administratorpasswort eingeben.
2 Akzeptieren Sie in den nächsten Fenstern jeweils die Standardoptionen, indem Sie auf Weiter klicken, und klicken Sie auf Agree, um die Lizenzbedingungen zu akzeptieren.
3 Klicken Sie im letzten Fenster auf Install.
Auf Ubuntu können Sie Python wie folgt im Terminal installieren:
1 Öffnen Sie ein Terminalfenster.
2 Geben Sie sudo apt-get install python3 ein.
3 Geben Sie sudo apt-get install idle3 ein.
4 Geben Sie sudo apt-get install python3-pip ein.
Mu herunterladen und installieren
Der Python-Interpreter ist die Software, die Ihre Python-Programme ausführt. Die Eingabe der Programme dagegen erfolgt im Editor Mu ähnlich wie in einer Textverarbeitung. Mu können Sie von https://codewith.mu/ herunterladen.
Auf Windows und macOS laden Sie die Installerdatei für Ihr Betriebssystem herunter und führen sie aus, indem Sie darauf doppelklicken. Auf macOS wird dadurch ein Fenster geöffnet, in dem Sie das Mu-Symbol in den Ordner Programm ziehen müssen, um mit der Installation fortzufahren. Auf Ubuntu installieren Sie Mu als Python-Paket. Klicken Sie dazu auf der Downloadseite im Abschnitt Python Package auf die Schaltfläche Instructions.
Mu starten
Um Mu zu starten, gehen Sie wie folgt vor:
Auf Windows 7 und höher klicken Sie auf das Startsymbol in der linken unteren Ecke, geben Mu in das Suchfeld ein und wählen die Anwendung aus.
Auf macOS öffnen Sie ein Finder-Fenster, klicken auf Programme und dann auf mu-editor.
Auf Ubuntu wählen Sie Anwendungen > Zubehör > Terminal und geben dann python3 -m mu ein.
Wenn Sie Mu zum ersten Mal ausführen, erscheint das Fenster Select Mode mit den Optionen Adafruit CircuitPython, BBC micro:bit, Pygame Zero und Python 3. Wählen Sie hier Python 3. Bei Bedarf können Sie den Modus später wieder ändern. Klicken Sie dazu auf die Schaltfläche Mode oben im Editorfenster.
Hinweis
Um die in diesem Buch verwendeten Drittanbietermodule installieren zu können, brauchen Sie Mu in der Version 1.1.0. Sie steht zurzeit als Alpha-Release unter einem eigenen Link getrennt von den anderen Downloadlinks zur Verfügung.
IDLE starten
In diesem Buch verwenden wir Mu als Editor und als interaktive Shell. Um Python-Code zu schreiben, können Sie jedoch auch beliebige andere Editoren verwenden. Zusammen mit Python wird die Software IDLE (Integrated Development and Learning Environment) installiert, die Sie ebenfalls als Editor verwenden können, wenn Sie Mu aus irgendeinem Grunde nicht installieren oder zum Laufen bekommen können. Um IDLE zu starten, gehen Sie wie folgt vor:
Auf Windows 7 und höher klicken Sie auf das Startsymbol in der linken unteren Ecke, geben IDLE in das Suchfeld ein und wählen IDLE (Python GUI) aus.
Auf macOS öffnen Sie ein Finder-Fenster, klicken auf Programme, dann auf Python 3.8 und schließlich auf das IDLE-Symbol.
Auf Ubuntu wählen Sie Anwendungen > Zubehör > Terminal und geben dann idle3 ein. (Möglicherweise können Sie auch oben auf dem Bildschirm auf Anwendungen klicken, dann Programming auswählen und auf IDLE3 klicken.)
Die interaktive Shell
Wenn Sie Mu ausführen, sehen Sie normalerweise das Editorfenster. Sie können in Mu allerdings auch eine interaktive Shell öffnen, indem Sie auf die Schaltfläche REPL klicken. Eine Shell ist ein Programm, in das Sie Anweisungen für den Computer eingeben können, ähnlich wie im Terminal von macOS oder an der Eingabeaufforderung von Windows. In die interaktive Python-Shell geben Sie die Anweisungen ein, die der Python-Interpreter ausführen soll. Der Computer liest diese Anweisungen und setzt sie sofort um.
In Mu ist die interaktive Shell ein Bereich in der unteren Hälfte des Fensters. Dort wird folgender Text angezeigt:
Jupyter QtConsole 4.3.1
Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit
(AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]:
Wenn Sie IDLE verwenden, erscheint als erstes Fenster die interaktive Shell. Sie ist bis auf den folgenden Text größtenteils leer:
Python 3.8.0b1 (tags/v3.8.0b1:3b5deb0116, Jun 4 2019, 19:52:55) [MSC v.1916
64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
Bei [1]: und >>> handelt es sich jeweils um die Eingabeaufforderung. In diesem Buch verwenden wir dafür das Zeichen >>>, da es gebräuchlicher ist. Auch im Terminal oder an der Windows-Eingabeaufforderung sehen Sie >>>. Die Eingabeaufforderung [1]: wurde für Jupyter Notebook erfunden, einen weiteren weitverbreiteten Python-Editor.
Zum Ausprobieren geben Sie an der Eingabeaufforderung der Shell Folgendes ein:
>>> print('Hello, world!')
Wenn Sie nun die Eingabetaste drücken, zeigt die interaktive Shell die Reaktion an:
>>> print('Hello, world!')
Hello, world!
Damit haben Sie Ihrem Computer gerade eine Anweisung erteilt, und er hat genau das getan, was Sie von ihm verlangt haben!
Drittanbietermodule installieren
Für manche Zwecke muss ein Programm Module importieren. Einige davon sind im Lieferumfang von Python enthalten, aber andere – sogenannte Drittanbietermodule – wurden von Programmierern erstellt, die nicht zum Hauptentwicklerteam von Python gehören. In Anhang A finden Sie eine ausführliche Anleitung, um solche Module mit dem Programm pip (auf Windows) bzw. pip3 (auf macOS und Linux) zu installieren. Wenn Sie in diesem Buch angewiesen werden, ein bestimmtes Drittanbietermodul zu installieren, schlagen Sie in Anhang A nach.
Hilfe finden
Programmierer neigen dazu, im Internet nach Antworten auf ihre Fragen zu suchen. Das ist eine ganz andere Art des Lernens, als viele es gewohnt sind. Es gibt hier keinen persönlich anwesenden Lehrer, der Ihnen etwas beibringt und Ihre Fragen beantwortet. Was das Internet als Klassenzimmer auszeichnet, ist die Tatsache, dass es dort sehr viele Menschen gibt, die Ihre Fragen beantworten können. Höchstwahrscheinlich ist Ihre Frage auch schon längst beantwortet worden, sodass die Lösung lediglich darauf wartet, dass Sie sie finden. Wenn Sie eine Fehlermeldung erhalten oder Schwierigkeiten haben, den Code das machen zu lassen, was er soll, sind Sie nicht die erste Person, die sich diesem Problem gegenübersieht. Daher ist es viel einfacher, als Sie glauben, eine Lösung zu finden.
Um Ihnen ein Beispiel zu geben, provozieren wir absichtlich einen Fehler: Geben Sie in die interaktive Shell '42' + 3 ein. Machen Sie sich keine Gedanken darüber, was diese Anweisung bedeutet und was daran falsch sein soll, sondern achten Sie auf das Ergebnis:
>>> '42' + 3
Traceback (most recent call last): 
File "<pyshell#0>", line 1, in <module>
'42' + 3
TypeError: Can't convert 'int' object to str implicitly 
>>>
Da Python die Anweisung nicht versteht, erscheint hier eine Fehlermeldung (
). Der als »Traceback« bezeichnete Teil der Fehlermeldung (
) gibt die Anweisung und die Nummer der Zeile an, mit der Python Schwierigkeiten hat. Wenn Sie eine Fehlermeldung erhalten, die Ihnen schleierhaft ist, suchen Sie online danach. In diesem Fall also würden Sie "TypeError: Can't convert 'int' object to str implicitly" (in Anführungszeichen) in eine Suchmaschine eingeben. Daraufhin sehen Sie haufenweise Links, in denen erklärt wird, was diese Fehlermeldung bedeutet und was die Ursache ist (siehe E–2).
Abb. 2–2Google-Ergebnisse zu einer Fehlermeldung können sehr hilfreich sein.
Sie werden dabei sehr oft bemerken, dass schon einmal jemand die gleiche Frage gestellt hat wie Sie und irgendeine hilfsbereite Person sie bereits beantwortet hat. Niemand kann alles über Programmierung wissen. Zur täglichen Arbeit eines Softwareentwicklers gehört auch die Suche nach Antworten auf technische Fragen.
Sinnvolle Fragen stellen
Wenn Sie die Antworten auf Ihre Fragen nicht durch eine Onlinesuche finden können, versuchen Sie, Teilnehmer in Webforen wie Stack Overflow (https://stackoverflow.com/) oder dem Subreddit »Learn Programming« auf https://reddit.com/r/learnprogramming/ zu fragen. Beachten Sie aber, dass Sie Ihre Fragen geschickt stellen müssen, damit andere Ihnen helfen können. Lesen Sie auf jeden Fall den FAQ-Abschnitt der Website, um zu erfahren, wie Sie Fragen auf richtige Weise vorbringen.
Wenn Sie Fragen zur Programmierung stellen, sollten Sie Folgendes tun:
Erklären Sie nicht nur, was Sie getan haben, sondern auch, was Sie tun wollten. Dadurch können Helfer erkennen, ob Sie sich verrannt haben.
Geben Sie genau an, wann der Fehler auftritt. Zeigt er sich gleich zu Beginn des Programms oder erst nach einer bestimmten Aktion?
Kopieren Sie die gesamte Fehlermeldung und Ihren Code auf https://pastebin.com/ oder https://gist.github.com/. Diese Websites erleichtern es, anderen Personen große Mengen an Code über das Web zur Verfügung zu stellen, ohne die Formatierung zu verlieren. Die URL zu dem dort veröffentlichten Code fügen Sie dann in Ihre E-Mail oder Ihren Forumspost ein. Als Beispiele können Sie sich Code von mir auf https://pastebin.com/SzP2DbFx/ und https://gist.github.com/asweigart/6912168/ ansehen.
Erklären Sie, was Sie bereits versucht haben, um das Problem zu lösen. Das zeigt den anderen, dass Sie selbst schon etwas Mühe darin investiert haben, die Lösung herauszufinden.
Geben Sie an, welche Version von Python Sie verwenden. (Es gibt einige entscheidende Unterschiede zwischen den Python-Interpretern der Versionen 2 und 3.) Nennen Sie auch die Version Ihres Betriebssystems.
Wenn ein Fehler nach einer Änderung am Code auftrat, erklären Sie, was Sie genau geändert haben.
Geben Sie an, ob der Fehler jedes Mal auftritt, wenn Sie das Programm ausführen, oder nur, nachdem Sie bestimmte Aktionen durchgeführt haben. Beschreiben Sie in letzterem Fall auch diese Aktionen.
Befolgen Sie immer die Online-Etikette. Schreiben Sie also Ihre Posts nicht komplett in Großbuchstaben und stellen Sie keine unsinnigen Forderungen an die Menschen, die Ihnen zu helfen versuchen.
Weitere Informationen darüber, wie Sie um Hilfe bei Programmierfragen bitten können, erhalten Sie in dem Blogpost auf https://autbor.com/help/. Eine Liste häufig gestellter Fragen über Programmierung finden Sie auf https://www.reddit.com/r/learnprogramming/wiki/faq/. Eine ähnliche Liste zu dem Thema, einen Job im Bereich der Softwareentwicklung zu bekommen, steht auf https://www.reddit.com/r/cscareerquestions/wiki/index/.
Ich helfe anderen gern dabei, Python kennenzulernen. So schreibe ich Programmiertutorials in meinem Blog auf https://inventwithpython.com/blog/. Sie können sich mit Ihren Fragen auch über al@inventwithpython.com an mich wenden (in Englisch). Eine schnellere Antwort dürften Sie allerdings erhalten, wenn Sie Ihre Fragen auf https://reddit.com/r/inventwithpython/ stellen.
Zusammenfassung
Für die meisten Menschen ist ein Computer eher ein Gerät als ein Werkzeug. Wenn Sie jedoch zu programmieren lernen, steht Ihnen eines der vielseitigsten Werkzeuge der modernen Welt zur Verfügung, und obendrein werden Sie auch noch Spaß dabei haben. Programmierung ist keine Gehirnchirurgie – Sie können sich auch als Anfänger daran versuchen und gefahrlos Fehler machen.
Für dieses Buch müssen Sie keinerlei Programmierkenntnisse mitbringen. Es kann aber sein, dass Sie Fragen haben, die über den behandelten Stoff hinausgehen. Die richtigen Fragen zu stellen und zu wissen, wo Sie Antworten finden können, ist für Programmierer von unschätzbarem Wert.
Fangen wir an!
Teil 1
1
Grundlagen von Python
Die Programmiersprache Python bietet eine breite Palette von syntaktischen Konstruktionen, Standardbibliotheksfunktionen und Möglichkeiten zur interaktiven Entwicklung. Zum Glück brauchen Sie sich um das meiste davon nicht zu kümmern, sondern müssen nur so viel lernen, dass Sie damit praktische kleine Programme schreiben können.
Allerdings müssen Sie, bevor Sie irgendetwas tun können, zunächst einige Grundlagen der Programmierung erlernen. Wie ein Zauberlehrling werden Sie vielleicht denken, dass einige dieser Grundlagen ziemlich undurchsichtig sind und dass es viel Mühe macht, sie sich anzueignen, aber diese Kenntnisse und etwas Übung werden Sie in die Lage versetzen, Ihren Computer wie einen Zauberstab zu nutzen und damit unglaublich erscheinende Dinge zu tun.
In einigen Beispielen in diesem Kapitel werden Sie dazu aufgefordert, etwas in die interaktive Shell, auch REPL (Read-Evaluate-Print Loop, also etwa »Lesen-Auswerten-Ausgeben-Schleife«) genannt, einzugeben. Damit können Sie eine Python-Anweisung nach der anderen ausführen und die Ergebnisse unmittelbar einsehen. Die Verwendung dieser Shell eignet sich hervorragend, um zu lernen, was die grundlegenden Python-Anweisungen bewirken. Nutzen Sie sie daher, während Sie das Buch durcharbeiten. Auf diese Weise können Sie sich den Stoff besser merken, als wenn Sie ihn nur lesen würden.
Ausdrücke in die interaktive Shell eingeben
Um die interaktive Shell auszuführen, können Sie den Editor Mu starten, den Sie beim Durcharbeiten der Installationsanleitungen im Vorwort heruntergeladen haben. Auf Windows öffnen Sie dazu das Startmenü, geben Mu ein und starten die gleichnamige Anwendung. Auf macOS öffnen Sie den Ordner Programme und doppelklicken darin auf Mu. Klicken Sie auf die Schaltfläche New und speichern Sie die leere Datei als blank.py. Wenn Sie diese leere Datei ausführen, indem Sie auf Run klicken oder
drücken, wird die interaktive Shell als neuer Bereich am unteren Rand des Mu-Fensters geöffnet. Dort sehen Sie die Eingabeaufforderung >>> der Shell.
Geben Sie dort 2 + 2 ein, um Python eine einfache Berechnung ausführen zu lassen. Das Mu-Fenster zeigt jetzt Folgendes an:
>>> 2 + 2
4
>>>
In Python wird etwas wie 2 + 2 als Ausdruck bezeichnet. Dies ist die einfachste Form von Programmieranweisungen in dieser Sprache. Ausdrücke setzen sich aus Werten (wie 2) und Operatoren (wie +) zusammen. Sie können stets ausgewertet, also auf einen einzigen Wert reduziert werden. Daher können Sie an allen Stellen im Python-Code, an denen ein Wert stehen soll, auch einen Ausdruck verwenden.
Im vorstehenden Beispiel wurde 2 + 2 zu dem Wert 4 ausgewertet. Ein einzelner Wert ohne Operatoren wird ebenfalls als Ausdruck angesehen, wird aber nur zu sich selbst ausgewertet:
>>> 2
2
Fehler sind kein Beinbruch
Wenn ein Programm Code enthält, den der Computer nicht versteht, stürzt es ab, woraufhin Python eine Fehlermeldung anzeigt. Ihren Computer können Sie dadurch jedoch nicht beschädigen. Daher brauchen Sie auch keine Angst vor Fehlern zu haben. Bei einem Absturz hält das Programm nur unerwartet an.
Wenn Sie mehr über eine bestimmte Fehlermeldung wissen wollen, können Sie online nach dem genauen Text suchen. Auf www.dpunkt.de/python_automatisieren_2/ finden Sie außerdem eine Liste häufig auftretender Python-Fehlermeldungen und ihrer Bedeutungen.
Es gibt eine Menge verschiedener Operatoren, die Sie in Python-Ausdrücken verwenden können. Tabelle 1–1 führt die arithmetischen Operatoren auf.
| Operator | Operation | Beispiel | Ergebnis |
| ** | Exponent | 2 ** 3 | 8 |
| % | Modulo/Rest | 22 % 8 | 6 |
| // | Integerdivision/abgerundeter Quotient | 22 // 8 | 2 |
| / | Division | 22 / 8 | 2.75 |
| * | Multiplikation | 3 * 5 | 15 |
| - | Subtraktion | 5 - 2 | 3 |
| + | Addition | 2 + 2 | 4 |
Tab. 1–1 Arithmetische Operatoren, geordnet vom höchsten zum niedrigsten Rang
Die Auswertungsreihenfolge oder Rangfolge der arithmetischen Operatoren in Python entspricht ihrer gewöhnlichen Rangfolge in der Mathematik: Als Erstes wird der Operator ** ausgewertet, dann die Operatoren *, /, // und % von links nach rechts, und schließlich die Operatoren + und - (ebenfalls von links nach rechts). Um die Auswertungsreihenfolge zu ändern, können Sie bei Bedarf Klammern setzen. Der Weißraum zwischen den Operatoren und Werten spielt in Python keine Rolle (außer bei den Einrückungen am Zeilenanfang). Ein Abstand von einem Leerzeichen ist jedoch üblich. Zur Übung geben Sie die folgenden Ausdrücke in die interaktive Shell ein:
>>> 2 + 3 * 6
20
>>> (2 + 3) * 6
30
>>> 48565878 * 578453
28093077826734
>>> 2 ** 8
256
>>> 23 / 7
3.2857142857142856
>>> 23 // 7
3
>>> 23 % 7
2
>>> 2 + 2
4
>>> (5 - 1) * ((7 + 1) / (3 - 1))
16.0
Die Ausdrücke müssen Sie jeweils selbst eingeben, aber Python nimmt Ihnen die Arbeit ab, sie auf einen einzelnen Wert zu reduzieren. Wie die folgende Grafik zeigt, wertet es dabei die einzelnen Teile eines Ausdrucks nacheinander aus, bis ein einziger Wert übrig ist:

Die Regeln, nach denen Operatoren und Werte zu Ausdrücken zusammengestellt werden, bilden einen grundlegenden Bestandteil der Programmiersprache Python, vergleichbar mit den Grammatikregeln einer natürlichen Sprache. Betrachten Sie das folgende Beispiel:
Dies ist ein grammatikalisch korrekter deutscher Satz.
Dies grammatikalisch ist Satz kein deutscher korrekter.
Der zweite Satz lässt sich nur schwer verstehen (»parsen«, wie es bei einer Programmiersprache heißt), da er nicht den Regeln der deutschen Grammatik folgt. Genauso ist es, wenn Sie eine schlecht formulierte Python-Anweisung eingeben. Python versteht sie nicht und zeigt die Fehlermeldung SyntaxError an, wie die folgenden Beispiele zeigen:
>>> 5 +
File "<stdin>", line 1
5 +
^
SyntaxError: invalid syntax
>>> 42 + 5 + * 2
File "<stdin>", line 1
42 + 5 + * 2
^
SyntaxError: invalid syntax
Um herauszufinden, ob eine Anweisung funktioniert oder nicht, können Sie sie einfach in die interaktive Shell eingeben. Keine Angst, dadurch können Sie nichts kaputt machen. Schlimmstenfalls zeigt Python eine Fehlermeldung an. Für professionelle Softwareentwickler gehören Fehlermeldungen zum Alltag.
Die Datentypen für ganze Zahlen, Fließkommazahlen und Strings
Ein Datentyp ist eine Kategorie für Werte, wobei jeder Wert zu genau einem Datentyp gehört. Die gebräuchlichsten Datentypen in Python finden Sie in Tabelle 1–2. Werte wie -2 und -30 sind beispielsweise Integerwerte. Dieser Datentyp (int) steht für ganze Zahlen. Zahlen mit Dezimalpunkt, z. B. 3.14, sind dagegen Fließkommazahlen und weisen den Typ float auf. Beachten Sie, dass ein Wert wie 42 ein Integer ist, 42.0 dagegen eine Fließkommazahl.
| Datentyp | Beispiele |
| Integer | -2, -1, 0, 1, 2, 3, 4, 5 |
| Fließkommazahlen | -1.25, -1.0, -0.5, 0.0, 0.5, 1.0, 1.25 |
| Strings | 'a', 'aa', 'aaa', 'Hello!', '11 cats' |
Tab. 1–2 Häufig verwendete Datentypen
In Python-Programmen können auch Textwerte vorkommen, sogenannte Strings (str). Schließen Sie Strings immer in einfache Anführungszeichen ein (z. B. 'Hello' oder 'Goodbye cruel world!'), damit Python weiß, wo der String anfängt und wo er endet. Sie können sogar einen String erstellen, der gar keine Zeichen enthält, nämlich den leeren String ''. In Kapitel 4 werden Strings ausführlicher behandelt.
Wenn Sie die Fehlermeldung SyntaxError: EOL while scanning string literal erhalten, haben Sie wahrscheinlich wie im folgenden Beispiel das schließende einfache Anführungszeichen am Ende eines Strings vergessen:
>>> 'Hello world!
SyntaxError: EOL while scanning string literal
Stringverkettung und -wiederholung
Die Bedeutung eines Operators kann sich in Abhängigkeit von den Datentypen der Werte ändern, die rechts und links von ihm stehen. Beispielsweise fungiert + zwischen zwei Integer- oder Fließkommawerten als Additionsoperator, zwischen zwei Strings aber als Stringverkettungsoperator. Probieren Sie Folgendes in der interaktiven Shell aus:
>>> 'Alice' + 'Bob'
'AliceBob'
Dieser Ausdruck wird zu einem einzigen neuen String ausgewertet, der den Text der beiden Originalstrings enthält. Wenn Sie jedoch versuchen, den Operator + zwischen einem String und einem Integerwert einzusetzen, weiß Python nicht, wie es damit umgehen soll, und gibt eine Fehlermeldung aus:
>>> 'Alice' + 42
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
'Alice' + 42
TypeError: can only concatenate str (not "int") to str
Die Fehlermeldung can only concatenate str (not "int") to str bedeutet, dass Python glaubt, Sie wollten einen Integer mit dem String 'Alice' verketten. Dazu aber müssten Sie den Integerwert ausdrücklich in einen String umwandeln, da Python dies nicht automatisch tun kann. (Die Umwandlung von Datentypen werden wir im Abschnitt »Analyse des Programms« weiter hinten in diesem Kapitel erklären und uns dabei mit den Funktionen str(), int() und float() beschäftigen.)
Zwischen zwei Integer- oder Fließkommawerten dient * als Multiplikationsoperator, doch zwischen einem String und einem Integerwert wird er zum Stringwiederholungsoperator. Um das auszuprobieren, geben Sie in die interaktive Shell Folgendes ein:
>>> 'Alice' * 5
'AliceAliceAliceAliceAlice'
Der Ausdruck wird zu einem einzigen String ausgewertet, der den ursprünglichen String so oft enthält, wie der Integerwert angibt. Die Stringwiederholung ist zwar ein nützlicher Trick, wird aber längst nicht so häufig angewendet wie die Stringverkettung.
Den Operator * können Sie nur zwischen zwei numerischen Werten (zur Multiplikation) oder zwischen einem String- und einem Integerwert einsetzen (zur Stringwiederholung). In allen anderen Fällen zeigt Python eine Fehlermeldung an:
>>> 'Alice' * 'Bob'
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
'Alice' * 'Bob'
TypeError: can't multiply sequence by non-int of type 'str'
>>> 'Alice' * 5.0
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
'Alice' * 5.0
TypeError: can't multiply sequence by non-int of type 'float'
Es ist sinnvoll, dass Python solche Ausdrücke nicht auswertet. Schließlich ist es nicht möglich, zwei Wörter miteinander zu multiplizieren, und es dürfte auch ziemlich schwierig sein, einen willkürlichen String eine gebrochene Anzahl von Malen zu wiederholen.
Werte in Variablen speichern
Eine Variable können Sie sich wie eine Kiste im Arbeitsspeicher des Computers vorstellen, in der einzelne Werte abgelegt werden. Wenn Sie das Ergebnis eines ausgewerteten Ausdrucks an einer späteren Stelle in Ihrem Programm noch brauchen, können Sie es in einer Variablen festhalten.
Zuweisungsanweisungen
Um einen Wert in einer Variablen zu speichern, verwenden Sie eine Zuweisungsanweisung. Sie besteht aus einem Variablennamen, einem Gleichheitszeichen (das hier nicht als Gleichheitszeichen dient, sondern als Zuweisungsoperator) und dem zu speichernden Wert. Wenn Sie die Zuweisungsanweisung spam = 42 eingeben, wird der Wert 42 in der Variablen spam gespeichert.
Sie können sich eine Variable als eine beschriftete Kiste vorstellen, in der der Wert abgelegt wird (siehe Abb. 1–1).
Abb. 1–1Die Anweisung spam = 42 sagt dem Programm: »Die Variable spam enthält jetzt die Ganzzahl 42.«
Geben Sie beispielsweise Folgendes in die interaktive Shell ein:
>>> spam = 40 
>>> spam
40
>>> eggs = 2
>>> spam + eggs 
42
>>> spam + eggs + spam
82
>>> spam = spam + 2 
>>> spam
42
Eine Variable wird initialisiert (erstellt), wenn zum ersten Mal ein Wert in ihr gespeichert wird (
). Danach können Sie sie zusammen mit anderen Variablen und Werten in Ausdrücken verwenden (
). Wenn Sie der Variablen einen neuen Wert zuweisen (
), geht der alte Wert verloren. Daher wird spam am Ende dieses Beispiels nicht mehr zu 40 ausgewertet, sondern zu 42. Die Variable ist also überschrieben worden. Versuchen Sie in der interaktiven Shell wie folgt einen String zu überschreiben:
>>> spam = 'Hello'
>>> spam
'Hello'
>>> spam = 'Goodbye'
>>> spam
'Goodbye'
Wie die Kiste in Abb. 1–2 enthält die Variable spam in diesem Beispiel den Wert Hello, bis er durch Goodbye ersetzt wird.
Abb. 1–2Wird einer Variablen ein neuer Wert zugewiesen, so wird der alte Wert vergessen.
Variablennamen
Ein guter Variablenname beschreibt die enthaltenen Daten. Stellen Sie sich vor, Sie ziehen um und beschriften alle Kartons mit »Sachen«. Dann würden Sie Ihre Sachen nie wiederfinden! In diesem Buch und in einem Großteil der Python-Dokumentation werden allgemeine Variablennamen wie spam, eggs und bacon verwendet (in Anlehnung an den Spam-Sketch von Monty Python), aber in Ihren eigenen Programmen sollten Sie beschreibende Namen verwenden, um den Code leichter lesbar zu machen.
Sie können Variablen in Python fast beliebig benennen, allerdings gibt es einige Einschränkungen. Tabelle 1–3 führt Beispiele für gültige Variablennamen auf. Sie müssen die folgenden drei Regeln erfüllen:
Der Name muss ein einzelnes Wort sein, darf also keine Leerzeichen enthalten.
Der Name darf nur aus Buchstaben, Ziffern und dem Unterstrich bestehen.
Der Name darf nicht mit einer Zahl beginnen.
| Gültige Variablennamen | Ungültige Variablennamen |
| current_balance | current-balance (Bindestriche sind nicht zulässig) |
| currentBalance | current balance (Leerzeichen sind nicht zulässig) |
| account4 | 4account (der Name darf nicht mit einer Zahl beginnen) |
| _42 | 42 (der Name darf nicht mit einer Zahl beginnen) |
| TOTAL_SUM | TOTAL_$UM (Sonderzeichen wie $ sind nicht zulässig) |
| hello | 'hello' (Sonderzeichen wie ' sind nicht zulässig) |
Tab. 1–3 Gültige und ungültige Variablennamen
Bei Variablennamen wird zwischen Groß- und Kleinschreibung unterschieden, sodass spam, SPAM, Spam und sPaM vier verschiedene Variablen bezeichnen. Zwar ist Spam ein zulässiger Variablenname, aber verabredungsgemäß sollten Variablennamen in Python mit einem Kleinbuchstaben beginnen.
In diesem Buch wird für Variablennamen die CamelCase-Schreibweise verwendet, also die Schreibung mit Binnenmajuskel statt mit einem Unterstrich. Variablennamen sehen also aus wie lookLikeThis und nicht wie look_like_this. Erfahrene Programmierer mögen einwenden, dass die offizielle Python-Stilrichtlinie PEP 8 Unterstriche verlangt. Ich bevorzuge allerdings die CamelCase-Schreibweise und möchte dazu auf den Abschnitt »Sinnlose Übereinstimmung ist die Plage kleiner Geister« aus PEP 8 verweisen:
»Übereinstimmung mit der Stilrichtlinie ist wichtig. Am wichtigsten ist es jedoch zu wissen, wann man diese Übereinstimmung aufgeben muss. Für manche Fälle ist die Stilrichtlinie einfach ungeeignet. Urteilen Sie dann selbst nach bestem Wissen und Gewissen.«
Ihr erstes Programm
In der interaktiven Shell können Sie einzelne Python-Anweisungen nacheinander ausführen, aber um ein vollständiges Python-Programm zu schreiben, müssen Sie die Anweisungen in den Dateieditor eingeben. Er ähnelt Texteditoren wie dem Windows-Editor oder TextMate, verfügt aber zusätzlich über einige Sonderfunktionen für die Eingabe von Quellcode. Um in Mu eine neue Datei anzulegen, klicken Sie in der obersten Zeile auf New.
In dem Fenster, das jetzt erscheint, sehen Sie einen Cursor, der auf Ihre Eingaben wartet. Dieses Fenster unterscheidet sich jedoch von der interaktiven Shell, in der Python-Anweisungen ausgeführt werden, sobald Sie die Eingabetaste drücken. Im Dateieditor können Sie viele Anweisungen eingeben, die Datei speichern und dann das Programm ausführen. Anhand der folgenden Merkmale können Sie erkennen, in welchem der beiden Fenster Sie sich gerade befinden:
Das Fenster der interaktiven Shell zeigt die Eingabeaufforderung >>> an.
Im Dateieditorfenster gibt es die Eingabeaufforderung >>> nicht.
Nun ist es an der Zeit, Ihr erstes Programm zu schreiben! Geben Sie im Fenster des Dateieditors Folgendes ein:
# Dieses Programm sagt "Hallo" und fragt nach Ihrem Namen. 
print('Hello world!') 
print('What is your name?') # Fragt nach dem Namen
myName = input() 
print('It is good to meet you, ' + myName) 
print('The length of your name is:') 
print(len(myName))
print('What is your age?') # Fragt nach dem Alter 
myAge = input()
print('You will be ' + str(int(myAge) + 1) + ' in a year.')
Nachdem Sie den Quellcode eingegeben haben, speichern Sie ihn, damit Sie ihn nicht jedes Mal neu eingeben müssen, wenn Sie Mu starten. Klicken Sie auf Save, geben Sie im Feld File Name den Namen hello.py ein und klicken Sie auf Save.
Während Sie ein Programm eingeben, sollten Sie es zwischendurch immer mal wieder speichern. Sollte Ihr Computer abstürzen oder sollten Sie versehentlich Mu beenden, verlieren Sie dann keinen Code. Als Tastaturkürzel zum Speichern einer Datei drücken Sie
+
auf Windows und Linux bzw.
+
auf macOS.
Nachdem Sie das Programm gespeichert haben, führen Sie es aus. Drücken Sie dazu
. Das Programm läuft jetzt im Fenster der interaktiven Shell. Beachten Sie aber, dass Sie
im Editorfenster drücken müssen, nicht im Shell-Fenster. Geben Sie Ihren Namen ein, wenn das Programm Sie danach fragt. Die Programmausgabe im Fenster der interaktiven Shell sieht wie folgt aus:
Python 3.7.0b4 (v3.7.0b4:eb96c37699, May 2 2018, 19:02:22) [MSC v.1913 64 bit
(AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
Hello world!
What is your name?
Al
It is good to meet you, Al
The length of your name is:
2
What is your age?
4
You will be 5 in a year.
>>>
Wenn keine Programmzeilen zum Ausführen mehr übrig sind, wird das Programm beendet.
Um den Dateieditor zu schließen, klicken Sie einfach auf das X in der rechten oberen Ecke. Wenn Sie ein gespeichertes Programm laden wollen, wählen Sie im Menü File > Open. Probieren Sie das jetzt aus. Wählen Sie in dem Fenster, das daraufhin erscheint, hello.py und klicken Sie auf Open. Jetzt erscheint wieder das zuvor gespeicherte Programm hello.py im Dateieditorfenster.
Wie ein Programm ausgeführt wird, können Sie in dem Visualisierungsprogramm Python Tutor auf http://pythontutor.com/ beobachten. Den Ablauf des vorhergehenden Programms finden Sie auf https://autbor.com/hellopy/ zur Einsicht. Klicken Sie jeweils auf die Weiter-Schaltfläche und bewegen Sie sich durch die einzelnen Schritte der Programmausführung. Dabei können Sie beobachten, wie sich die Werte der Variablen und die Ausgabe ändern.
Analyse des Programms
Anhand Ihres neuen Programms im Dateieditor wollen wir uns genauer ansehen, was die einzelnen Programmzeilen bewirken und wozu die einzelnen Python-Anweisungen da sind.
Kommentare
Die erste Zeile ist ein sogenannter Kommentar:
# Dieses Programm sagt "Hallo" und fragt nach Ihrem Namen. 
Python ignoriert Kommentare. Sie können sie dazu verwenden, um Anmerkungen zu machen und sich selbst daran zu erinnern, was der Code tun soll. Der gesamte restliche Text einer Zeile, die mit dem Zeichen # beginnt, gehört zum Kommentar.
Beim Testen von Programmen stellen Programmierer manchmal auch ein # vor eine Zeile mit Code, um sie vorübergehend zu entfernen. Diese Vorgehensweise wird Auskommentieren genannt. Das kann sehr hilfreich sein, wenn Sie herausfinden wollen, warum ein Programm nicht funktioniert. Wenn Sie später die Codezeile wieder in das Programm aufnehmen möchten, können Sie das # einfach löschen.
Python ignoriert auch die Leerzeile unter dem Kommentar. Sie können in Ihr Programm so viele Leerzeilen einbauen, wie Sie wollen. Ähnlich wie Absätze in einem Buch machen sie den Code leichter lesbar.
Die Funktion print()
Die Funktion print() gibt den in den Klammern angegebenen Stringwert auf dem Bildschirm aus:
print('Hello world!') 
print('What is your name?') # Fragt nach dem Namen
Die Zeile print('Hello world!') bedeutet: »Gib den Text des Strings 'Hello World!' aus.« Wenn Python diese Zeile ausführt, ruft Python die Funktion print() auf und übergibt ihr den Stringwert. Einen Wert, der an einen Funktionsaufruf übergeben wird, bezeichnet man als Argument. Beachten Sie, dass die Anführungszeichen nicht auf dem Bildschirm ausgegeben werden. Sie markieren nur den Anfang und das Ende des Strings, sind aber nicht Bestandteil des Stringwertes.
Hinweis
Mit dieser Funktion können Sie auch eine leere Zeile auf dem Bildschirm ausgeben. Rufen Sie dazu einfach print() auf, also ohne irgendeinen Inhalt zwischen den Klammern.
Das Klammernpaar hinter einem Namen zeigt an, dass es sich um den Namen einer Funktion handelt. Aus diesem Grund sehen Sie in diesem Buch überall Bezeichnungen wie print() statt print. Funktionen werden in Kapitel 3 ausführlicher beschrieben.
Die Funktion input()
Die Funktion input() wartet darauf, dass der Benutzer Text über die Tastatur eingibt und die Eingabetaste drückt.
myName = input() 
Sie können sich den Funktionsaufruf input() als einen Ausdruck vorstellen, der zu dem vom Benutzer eingegebenen String ausgewertet wird. Gibt der Benutzer beispielsweise Al ein, so wird der Ausdruck zu myName = 'Al' ausgewertet. In dem vorstehenden Code wird der Stringwert der Variablen myName zugewiesen.
Wenn Sie input() aufrufen und eine Fehlermeldung wie NameError: name 'Al' is not defined erhalten, liegt das Problem wahrscheinlich daran, dass Sie den Code mit Python 2 statt mit Python 3 ausführen.
Den Benutzernamen ausgeben
Bei dem anschließenden Aufruf von print() steht der Ausdruck 'It is good to meet you, ' + myName in den Klammern:
print('It is good to meet you, ' + myName) 
Denken Sie daran, dass Ausdrücke immer zu einem einzigen Wert ausgewertet werden. Wenn in Zeile
der Wert 'Al' in myName gespeichert wurde, dann ergibt dieser Ausdruck 'It is good to meet you, Al'. Dieser Stringwert wird nun an die Funktion print() übergeben, die ihn auf dem Bildschirm anzeigt.
Die Funktion len()
Wenn Sie der Funktion len() einen Stringwert übergeben (oder eine Variable, die einen String enthält), wertet sie ihn zu einem Integer aus, der die Anzahl der Zeichen in diesem String angibt:
print('The length of your name is:') 
print(len(myName))
Um das auszuprobieren, geben Sie Folgendes in die interaktive Shell ein:
>>> len('hello')
5
>>> len('My very energetic monster just scarfed nachos.')
46
>>> len('')
0
Wie in diesen Beispielen wird auch len(myName) zu einem Integer ausgewertet, der dann an print() übergeben wird, um ihn auf dem Bildschirm auszugeben. An print() lassen sich sowohl Integer- als auch Stringwerte übergeben. Schauen Sie sich aber die Fehlermeldung an, die sich ergibt, wenn Sie Folgendes in die interaktive Shell eingeben:
>>> print('I am ' + 29 + ' years old.')
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
print('I am ' + 29 + ' years old.')
TypeError: can only concatenate str (not "int") to str
Es ist nicht die Funktion print(), die den Fehler verursacht, sondern der Ausdruck, den Sie ihr zu übergeben versuchen. Dieselbe Fehlermeldung erhalten Sie auch, wenn Sie den Ausdruck ganz für sich allein in die Shell eingeben:
>>> 'I am ' + 29 + ' years old.'
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
'I am ' + 29 + ' years old.'
TypeError: can only concatenate str (not "int") to str
Python meldet einen Fehler, da der Operator + nur zwei Integer addieren oder zwei Strings verketten kann. Der Versuch, einen Integer und einen String zu verketten, widerspricht dagegen der Grammatik von Python. Diesen Fehler können Sie korrigieren, indem Sie eine Stringversion des Integers verwenden. Wie das geht, sehen wir uns im nächsten Abschnitt an.
Die Funktionen str(), int() und float()
Wenn Sie eine Zahl wie 29 mit einem String verketten wollen, etwa um das Ergebnis an print() zu übergeben, brauchen Sie die Stringversion von 29, also '29'. Die Funktion str() nimmt einen Integer entgegen und wertet ihn zu seiner Stringversion aus:
>>> str(29)
'29'
>>> print('I am ' + str(29) + ' years old.')
I am 29 years old.
Da str(29) den String '29' ergibt, wird der Ausdruck 'I am ' + str(29) + ' years old' zu 'I am ' + '29' + ' years old' ausgewertet und dieses wiederum zu 'I am 29 years old'. Dieser Wert wird dann an print() übergeben.
Die Funktionen str(), int() und float() werden zu der String-, Integer- bzw. Fließkommaversion des übergebenen Wertes ausgewertet. Versuchen Sie in der interaktiven Shell, einige Werte mithilfe dieser Funktionen umzuwandeln, und beobachten Sie, was passiert.
>>> str(0)
'0'
>>> str(-3.14)
'-3.14'
>>> int('42')
42
>>> int('-99')
-99
>>> int(1.25)
1
>>> int(1.99)
1
>>> float('3.14')
3.14
>>> float(10)
10.0
In den vorstehenden Beispielen werden die Funktionen str(), int() und float() aufgerufen und ihnen Werte anderer Datentypen übergeben, aus denen sie Strings, Integer bzw. Fließkommazahlen machen.
Die Funktion str() ist insbesondere dann praktisch, wenn Sie eine Integer- oder Fließkommazahl haben, die Sie mit einem String verketten wollen. Liegt umgekehrt eine Zahl als Stringwert vor, dann können Sie die Funktion int() anwenden, um diese Zahl in mathematischen Funktionen einsetzen zu können. Das ist beispielsweise bei der Verwendung der Funktion input() wichtig, die stets einen String zurückgibt, auch wenn der Benutzer eine Zahl eingibt. Geben Sie in der interaktiven Shell spam = input() ein. Wenn die Shell auf Ihren Text wartet, schreiben Sie 101. Dabei geschieht Folgendes:
>>> spam = input()
101
>>> spam
'101'
In spam ist nicht etwa der Integer 101 gespeichert, sondern der String '101'. Wenn Sie mit dem Wert dieser Variablen nun irgendwelche Berechnungen anstellen wollen, müssen Sie ihn zunächst mit int() in die Integerform umwandeln und diese als neuen Wert in spam speichern.
>>> spam = int(spam)
>>> spam
101
Jetzt können Sie die Variable spam wie einen Integer verwenden und nicht mehr wie einen String:
>>> spam * 10 / 5
202.0
Wenn Sie int() einen Wert übergeben, der nicht in einen Integer umgewandelt werden kann, zeigt Python eine Fehlermeldung an.
>>> int('99.99')
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
int('99.99')
ValueError: invalid literal for int() with base 10: '99.99'
>>> int('twelve')
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
int('twelve')
ValueError: invalid literal for int() with base 10: 'twelve'
Mit der Funktion int() können Sie auch einen Fließkommawert abrunden.
>>> int(7.7)
7
>>> int(7.7) + 1
8
In Ihrem ersten Programm werden die Funktionen int() und str() in den letzten drei Zeilen verwendet, um im Code Werte des passenden Datentyps bereitzustellen.
print('What is your age?') # Fragt nach dem Alter 
myAge = input()
print('You will be ' + str(int(myAge) + 1) + ' in a year.')
Die Variable myAge enthält den von input() zurückgegebenen Wert. Da diese Funktion immer einen String zurückgibt (auch wenn der Benutzer eine Zahl eingegeben hat), müssen Sie den String in myAge mit int(myAge) in einen Integerwert umwandeln, damit Sie 1 addieren können, was in dem Ausdruck int(myAge) + 1 geschieht.
Das Ergebnis dieser Addition wird wiederum der Funktion str() übergeben: str(int(myAge) + 1). Der daraus resultierende Stringwert wird mit den Strings 'You will be ' und ' in a year' zu einem einzigen, langen Stringwert verkettet, der dann schließlich zur Anzeige an print() übergeben wird.
Nehmen wir an, der Benutzer gibt als Alter 4 ein. Der String '4' wird in einen Integer umgewandelt, sodass 1 addiert werden kann, was 5 ergibt. Die Funktion str() konvertiert dieses Ergebnis wieder in einen String zurück, sodass er mit dem zweiten String ' in a year' verkettet werden kann, um die endgültige Meldung zu bilden. Die Auswertung läuft wie folgt ab:

Das Verhältnis zwischen Text und Zahlen
Der Stringwert einer Zahl ist etwas völlig anderes als ihr Integer- oder Fließkommawert. Dagegen können Integer- und Fließkommawerte aber durchaus gleich sein.
>>> 42 == '42'
False
>>> 42 == 42.0
True
>>> 42.0 == 0042.000
True
Python macht diese Unterscheidung, da Strings Text darstellen, Integer- und Fließkommawerte aber Zahlen.
Zusammenfassung
Sie können arithmetische Ausdrücke mit einem Taschenrechner berechnen und Strings in einer Textverarbeitung verketten. Durch Kopieren und Einfügen können Sie sogar auf ganz einfache Weise eine Stringwiederholung erreichen. Ausdrücke, die aus Operatoren, Variablen und Funktionsaufrufen bestehen können, sind dagegen die Grundbausteine von Programmen. Wenn Sie mit diesen Elementen umgehen können, sind Sie in der Lage, Python anzuweisen, große Datenmengen für Sie zu verarbeiten.
Was Sie sich auf jeden Fall merken sollten, sind die verschiedenen Arten von Operatoren (die arithmetischen Operatoren +, -, *, /, //, % und ** sowie die Stringoperatoren + und *) und die drei in diesem Kapitel vorgestellten Datentypen (Integer, Fließkommazahlen und Strings).
Sie haben auch schon einige Funktionen kennengelernt. print() kümmert sich um die einfache Textausgabe (auf dem Bildschirm), input() um die Eingabe (von der Tastatur). Die Funktion len() nimmt einen String entgegen und wertet ihn zu einem Integer aus, der die Anzahl der Zeichen in dem String wiedergibt. Mit str(), int() und float() ermitteln Sie die String-, Integer- bzw. Fließkommaversion des übergebenen Wertes.
Im nächsten Kapitel lernen Sie, wie Sie in Python auf der Grundlage eines Wertes entscheiden, welcher Code ausgeführt, übersprungen oder wiederholt werden soll. Dies wird als Flusssteuerung bezeichnet. Damit können Sie Programme schreiben, die Entscheidungen treffen.
Wiederholungsfragen
1 Welche der folgenden Einträge sind Operatoren und welche sind Werte?*'hello'-88.8-/+5
2 Welcher der folgenden Einträge ist eine Variable und welcher ein String?spam'spam'
3 Nennen Sie drei Datentypen!
4 Woraus besteht ein Ausdruck? Was machen alle Ausdrücke?
5 In diesem Kapitel wurden Zuweisungsanweisungen wie spam = 10 vorgestellt. Was ist der Unterschied zwischen einem Ausdruck und einer Anweisung?
6 Welchen Wert enthält die Variable bacon, nachdem der folgende Code ausgeführt worden ist?bacon = 20bacon + 1
7 Wozu werden die beiden folgenden Ausdrücke ausgewertet?'spam' + 'spamspam''spam' * 3
8 Warum ist eggs ein gültiger Variablenname, 100 dagegen nicht?
9 Mit welchen drei Funktionen können Sie die Integer-, Fließkomma- oder Stringversion eines Wertes ermitteln?
10 Warum ruft der folgende Ausdruck eine Fehlermeldung hervor? Wie können Sie ihn korrigieren?'I have eaten ' + 99 + ' burritos.'
Zusatzpunkt: Suchen Sie online nach der Python-Dokumentation für die Funktion len(). Sie befindet sich auf einer Webseite mit dem Titel »Built-in Functions«. Schauen Sie sich in der Liste weitere Funktionen von Python an, schlagen Sie nach, was die Funktion round() macht, und experimentieren Sie damit in der interaktiven Shell.
2
Flusssteuerung
Sie kennen jetzt die Grundlagen von Anweisungen und wissen, dass ein Programm nichts anderes als eine Abfolge solcher Anweisungen ist. Die wahre Stärke der Programmierung besteht aber nicht darin, einfach nur eine Anweisung nach der anderen auszuführen, etwa so, wie Sie eine Einkaufsliste abarbeiten. Je nach dem, welches Ergebnis die Auswertung eines Ausdrucks ergibt, kann das Programm entscheiden, Anweisungen zu überspringen oder zu wiederholen, oder unter mehreren möglichen Anweisungen auswählen. In der Praxis wird ein Programm so gut wie nie von der ersten bis zur letzten Anweisung einfach stur Zeile für Zeile ausgeführt. Stattdessen wird mithilfe von Flusssteuerungsanweisungen entschieden, welche Anweisungen unter welchen Bedingungen auszuführen sind.
Da diese Flusssteuerungsanweisungen unmittelbar den Symbolen in einem Flussdiagramm entsprechen, zeige ich Ihnen in diesem Kapitel auch immer das Flussdiagramm zu dem besprochenen Code. Zur Einführung enthält Abb. 2–1 das Diagramm, um zu entscheiden, was zu tun ist, wenn es regnet. Folgen Sie dem Pfad entlang der Pfeile vom Anfang bis zum Ende.
Abb. 2–1Ein Flussdiagramm, das Ihnen sagt, was Sie tun müssen, wenn es regnet.
In einem Flussdiagramm gibt es gewöhnlich mehr als einen Weg, um vom Start zum Ende zu gelangen. Das Gleiche gilt auch für die Codezeilen in einem Computerprogramm. In Flussdiagrammen werden diese Verzweigungen durch Rauten dargestellt. Für die anderen Schritte werden Rechtecke verwendet, für Anfang und Ende abgerundete Rechtecke.
Bevor Sie die Flusssteuerungsanweisungen kennenlernen, müssen Sie zunächst einmal wissen, wie Sie die Optionen ja und nein darstellen und wie Sie die Verzweigungspunkte als Python-Code schreiben. Dazu beschäftigen wir uns mit booleschen Werten, Vergleichsoperatoren und booleschen Operatoren.
Boolesche Werte
Integer, Fließkommazahlen und Strings können unendlich viele mögliche Werte annehmen, doch für den booleschen Datentyp (benannt nach dem Mathematiker George Boole) gibt es nur zwei, nämlich wahr und falsch (True und False). In Python-Code werden die booleschen Werte True und False immer ohne die Anführungszeichen für Strings und immer mit großem Anfangsbuchstaben geschrieben. Der Rest des Wortes steht jeweils in Kleinbuchstaben. Geben Sie zum Ausprobieren folgenden Code in die interaktive Shell ein (wobei einige dieser Anweisungen absichtlich nicht korrekt sind und Fehlermeldungen hervorrufen):
>>> spam = True 
>>> spam
True
>>> true 
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
true
NameError: name 'true' is not defined
>>> True = 2 + 2 
SyntaxError: assignment to keyword
Wie alle anderen Werte können auch boolesche Werte in Ausdrücken verwendet und in Variablen gespeichert werden (
). Wenn Sie Groß- und Kleinschreibung verwechseln (
) oder wenn Sie versuchen, True oder False als Variablennamen zu verwenden (
), gibt Python eine Fehlermeldung aus.
Vergleichsoperatoren
Vergleichsoperatoren vergleichen zwei Werte, wobei das Ergebnis ein einzelner boolescher Wert ist. Tabelle 2–1 führt die möglichen Vergleichsoperatoren auf.
| Operator | Bedeutung |
| == | Gleich |
| != | Ungleich |
| < | Kleiner als |
| > | Größer als |
| <= | Kleiner oder gleich |
| >= | Größer oder gleich |
Tab. 2–1 Vergleichsoperatoren
Je nachdem, welche Werte Sie übergeben, werden diese Operatoren zu True oder False ausgewertet. Im Folgenden wollen wir einige Operatoren ausprobieren, wobei wir mit == und != beginnen.
>>> 42 == 42
True
>>> 42 == 99
False
>>> 2 != 3
True
>>> 2 != 2
False
Wie zu erwarten ist, wird == (gleich) zu True ausgewertet, wenn die Werte auf beiden Seiten gleich sind, != (ungleich) dagegen, wenn sie verschieden sind. Die Operatoren == und != können für Werte beliebiger Datentypen verwendet werden.
>>> 'hello' == 'hello'
True
>>> 'hello' == 'Hello'
False
>>> 'dog' != 'cat'
True
>>> True == True
True
>>> True != False
True
>>> 42 == 42.0
True
>>> 42 == '42' 
False
Beachten Sie, dass Integer- und Fließkommawerte immer ungleich den Stringwerten sind. Der Ausdruck 42 == '42' (
) wird zu False ausgewertet, da für Python der Integer 42 und der String '42' zwei verschiedene Dinge sind.
Die Operatoren <, >, <= und >= dagegen funktionieren nur bei Integer- und Fließkommawerten.
>>> 42 < 100
True
>>> 42 > 100
False
>>> 42 < 42
False
>>> eggCount = 42
>>> eggCount <= 42 
True
>>> myAge = 29
>>> myAge >= 10 
True
Der Unterschied zwischen den Operatoren == und =
Wahrscheinlich ist Ihnen schon aufgefallen, dass der Gleichheitsoperator == aus zwei Gleichheitszeichen besteht, der Zuweisungsoperator = dagegen nur aus einem. Diese beiden Operatoren lassen sich sehr leicht verwechseln. Merken Sie sich einfach Folgendes:
Mit dem Operator == (Gleichheit) fragen Sie, ob zwei Werte gleich sind.
Mit dem Operator = (Zuweisung) weisen Sie den Wert auf der rechten Seite der Variablen auf der linken Seite zu.
Um sich zu merken, welcher dieser Operatoren welcher ist, denken Sie daran, dass der Gleichheitsoperator == ebenso wie der Ungleichheitsoperator != aus zwei Zeichen besteht.
Häufig verwenden Sie Vergleichsoperatoren, um einen Wert mit einem anderen zu vergleichen, der in einer Variablen gespeichert ist, so wie in den Beispielen eggCount <= 42 (
) und myAge >= 10 (
). (Schließlich brauchen Sie so etwas wie 'dog' != 'cat' gar nicht ausdrücklich in Ihrem Code zu schreiben, sondern könnten stattdessen auch gleich True sagen.) Weitere Beispiele dazu werden Sie sehen, wenn wir uns mit Flusssteuerungsstrukturen beschäftigen.
Boolesche Operatoren
Die drei booleschen Operatoren and, or und not dienen dazu, boolesche Werte zu vergleichen. Wie Vergleichsoperatoren werten auch sie die betreffenden Ausdrücke zu einem einzigen booleschen Wert aus. Sehen wir uns diese Operatoren nun etwas genauer an. Dabei beginnen wir mit dem Operator and.
Binäre boolesche Operatoren
Da and und or stets zwei boolesche Werte (oder Ausdrücke) entgegennehmen, werden sie als binäre Operatoren bezeichnet. Dabei wird ein Ausdruck mit and zu True ausgewertet, wenn beide booleschen Werte True sind; anderenfalls ist der Ausdruck False. Um das auszuprobieren, geben Sie einige Ausdrücke mit and in die interaktive Shell ein:
>>> True and True
True
>>> True and False
False
Eine Wahrheitswertetafel zeigt alle möglichen Ergebnisse eines booleschen Operators. In Tabelle 2–2 sehen Sie die Wahrheitswertetafel für den Operator and.
| Ausdruck | Ergebnis |
| True and True | True |
| True and False | False |
| False and True | False |
| False and False | False |
Tab. 2–2Wahrheitswertetafel für den Operator and
Ein Ausdruck mit dem Operator or dagegen wird zu True ausgewertet, wenn mindestens einer der beiden booleschen Werte True ist. Nur wenn beide False sind, ist der Ausdruck False.
>>> False or True
True
>>> False or False
False
Alle möglichen Ergebnisse des Operators or sehen Sie in der Wahrheitswertetafel in Tabelle 2–3.
| Ausdruck | Ergebnis |
| True or True | True |
| True or False | True |
| False or True | True |
| False or False | False |
Tab. 2–3Wahrheitswertetafel für den Operator or
Der Operator not
Anders als and und or wird der Operator not nur auf einen einzelnen booleschen Wert (oder einen Ausdruck) angewendet und daher als unär bezeichnet. Das Ergebnis ist einfach der gegenteilige boolesche Wert.
>>> not True
False
>>> not not not not True 
True
Ebenso wie bei doppelten Verneinungen in Schrift und Sprache können Sie auch not-Operatoren verschachteln (
), allerdings gibt es nie nicht keinen Grund dafür, so etwas in einem Programm zu tun. Tabelle 2–4 zeigt die Wahrheitswertetafel für not.
| Ausdruck | Ergebnis |
| not True | False |
| not False | True |
Tab. 2–4Wahrheitswertetafel für den Operator not
Kombinierte Verwendung von booleschen und Vergleichsoperatoren
Da Vergleichsoperatoren zu booleschen Werten ausgewertet werden, können Sie sie in Ausdrücken mit booleschen Operatoren verwenden.
Denken Sie daran, dass die booleschen Operatoren and, or und not immer auf den booleschen Werten True und False operieren. Ausdrücke wie 4 < 5 sind zwar selbst keine booleschen Werte, werden aber zu booleschen Werten ausgewertet. Versuchen Sie, einige boolesche Ausdrücke mit Vergleichsoperatoren in die interaktive Shell einzugeben:
>>> (4 < 5) and (5 < 6)
True
>>> (4 < 5) and (9 < 6)
False
>>> (1 == 2) or (2 == 2)
True
Der Computer wertet als Erstes den linken und dann den rechten Ausdruck aus. Wenn er die booleschen Werte für beide Ausdrücke kennt, wertet er den Gesamtausdruck zu einem einzigen booleschen Wert aus. Den Auswertungsvorgang für (4 < 5) and (5 < 6) können Sie sich wie folgt vorstellen:

In einem Ausdruck können Sie auch mehrere boolesche Operatoren sowie Vergleichsoperatoren verwenden:
>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2
True
Ebenso wie für arithmetische Operatoren gibt es auch eine Verarbeitungsreihenfolge für boolesche Operatoren. Nachdem alle arithmetischen und Vergleichsoperatoren ausgewertet wurden, verarbeitet Python als Erstes not, danach die and- und schließlich die or-Operatoren.
Elemente zur Flusssteuerung
Die meisten Flusssteuerungsanweisungen beginnen mit einer Bedingung, auf die ein Codeblock folgt, die sogenannte Klausel. Bevor Sie die einzelnen Flusssteuerungsanweisungen in Python kennenlernen, sehen wir uns an, was Bedingungen und Codeblöcke eigentlich sind.
Bedingungen
Alle bisher vorgestellten booleschen Ausdrücke können auch als Bedingungen betrachtet werden. Eine Bedingung ist eine Art von Ausdruck im Zusammenhang mit Flusssteuerungsanweisungen. Bedingungen werden immer zu einem booleschen Wert ausgewertet, also zu True oder False, auf deren Grundlage die Flusssteuerungsanweisung dann entscheidet, was zu tun ist. Fast alle Flusssteuerungsanweisungen greifen auf eine Bedingung zurück.
Codeblöcke
In Python können Codezeilen zu Blöcken gruppiert werden. Wo ein Blick anfängt und wo er endet, können Sie an den Einrückungen der Codezeilen erkennen. Es gibt die folgenden drei Regeln für Blöcke:
Ein Block beginnt, wenn die Codezeilen weiter eingerückt werden.
Blöcke können andere Blöcke einschließen.
Ein Block endet, wenn die Einrückung auf null oder auf die Einrückung des einschließenden Blocks zurückgeht.
Das Prinzip von Blöcken lässt sich leichter verstehen, wenn Sie sich eingerückten Code ansehen. Suchen Sie die Blöcke in diesem Programmausschnitt:
name = 'Mary'
password = 'swordfish'
if name == 'Mary':
print('Hello, Mary') 
if password == 'swordfish':
print('Access granted.') 
else:
print('Wrong password.') 
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/blocks/ ansehen. Der erste Codeblock (
) beginnt mit der Zeile print('Hello, Mary') und enthält alle darauf folgenden Codezeilen. In diesem Block gibt es einen weiteren Block (
), der nur aus einer einzigen Zeile besteht, nämlich print('Access granted.'). Der dritte Block ist (
) ist ebenfalls nur eine Zeile lang: print('Wrong password.').
Programmausführung
Im Programm hello.py aus dem vorigen Kapitel hat Python mit der Ausführung der Anweisungen am Anfang des Programms begonnen und sich dann eine Anweisung nach der anderen vorgearbeitet. Wenn Sie den Quellcode auf Papier ausgeben und mit dem Finger den Zeilen folgen, während sie abgearbeitet werden, markiert Ihr Finger jeweils die Stelle, bei der die Programmausführung gerade angekommen ist.
Kaum ein Programm wird jedoch einfach dadurch ausgeführt, dass die Anweisungen in einer Reihe nacheinander abgearbeitet werden. Wenn Sie mit Ihrem Finger ein Programm nachvollziehen, das Flusssteuerungsanweisungen enthält, werden Sie je nach den vorliegenden Bedingungen hierhin und dorthin springen und ganze Klauseln auslassen.
Flusssteuerungsanweisungen
Sehen wir uns nun die wichtigsten Bestandteile der Flusssteuerung an, nämlich die Anweisungen dafür. Sie entsprechen den Rauten in dem Flussdiagramm aus Abb. 2–1 und sind die Stellen, an denen das Programm Entscheidungen trifft.
If-Anweisungen
Die am häufigsten verwendete Flusssteuerungsanweisung ist die if-Anweisung. Ihre Klausel (also der Block, der auf die Bedingung folgt) wird ausgeführt, wenn die Bedingung True ist, anderenfalls wird sie übersprungen.
In einfachem Deutsch kann eine if-Anweisung wie folgt formuliert werden: »Wenn diese Bedingung wahr ist, dann führe den Code in der Klausel aus.« In Python besteht eine if-Anweisung immer aus folgenden Elementen:
Dem Schlüsselwort if
Einer Bedingung (einem Ausdruck, der zu True oder False ausgewertet wird)
Einem Doppelpunkt
Einem eingerückten Codeblock (die if-Klausel), der in der nächsten Zeile beginnt
Beispielsweise prüft der folgende Code, ob der Name einer Person Alice ist (wobei Sie voraussetzen müssen, dass der Variablen name zuvor ein Wert zugewiesen wurde):
if name == 'Alice':
print('Hi, Alice.')
Alle Flusssteuerungsanweisungen enden mit einem Doppelpunkt, auf den ein neuer Codeblock (die Klausel) folgt. In unserem Beispiel ist dies der Block print('Hi, Alice.'). In Abb. 2–2 sehen Sie das Flussdiagramm zu diesem Code.
Abb. 2–2Flussdiagramm einer if-Anweisung
Else-Anweisungen
Auf eine if-Klausel kann optional auch eine else-Anweisung folgen. Die else-Klausel wird nur dann ausgeführt, wenn die Bedingung der if-Anweisung False ist. Eine else-Anweisung entspricht also folgender Formulierung: »Wenn diese Bedingung wahr ist, führe diesen Code aus, wenn nicht, dann jenen Code.« Die else-Anweisung braucht keine Bedingung. Sie weist immer die folgenden Bestandteile auf:
Das Schlüsselwort else
Einen Doppelpunkt
Einen eingerückten Codeblock (die else-Klausel), der in der nächsten Zeile beginnt
In unserem Alice-Beispiel können wir eine else-Anweisung verwenden, um eine andere Begrüßung anzuzeigen, wenn die Person nicht den Namen Alice hat:
if name == 'Alice':
print('Hi, Alice.')
else:
print('Hello, stranger.')
Das Flussdiagramm für diesen Code sehen Sie in Abb. 2–3.
Abb. 2–3Flussdiagramm einer else-Anweisung
Elif-Anweisungen
Bei den bisherigen Anweisungen wird entweder die if- oder die else-Klausel ausgeführt, doch es kann auch der Fall sein, dass Sie eine von vielen möglichen Klauseln ausführen lassen wollen. Die elif-Anweisung (»else if«) kann hinter einer if- oder einer anderen elif-Anweisung stehen, um eine weitere Bedingung zu prüfen, falls die vorherigen alle False waren. Eine elif-Anweisung weist immer folgende Bestandteile auf:
Das Schlüsselwort elif
Eine Bedingung (einen Ausdruck, der zu True oder False ausgewertet wird)
Einen Doppelpunkt
Einen eingerückten Codeblock (die elif-Klausel), der in der nächsten Zeile beginnt
Um die elif-Anweisung in Aktion zu erleben, fügen wir sie unserem Namensprüfungsprogramm hinzu:
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
Dieses Mal wird das Alter der Person geprüft, und wenn sie jünger als 12 ist, teilt das Programm ihr etwas anderes mit. Das Flussdiagramm dafür sehen Sie in Abb. 2–4.
Abb. 2–4Flussdiagramm einer elif-Anweisung
Die elif-Klausel wird ausgeführt, wenn age < 12 den Wert True ergibt und name == 'Alice' den Wert False. Wenn aber beide Bedingungen False sind, dann werden beide Klauseln übersprungen. Es gibt keine Garantie dafür, dass wenigstens eine ausgeführt wird. Bei einer Kette von elif-Anweisungen wird höchstens eine einzige davon ausgeführt. Sobald eine Bedingung dieser Anweisungen zu True ausgewertet wird, werden alle restlichen elif-Klauseln verworfen. Um sich das in einem Beispiel anzusehen, öffnen Sie ein neues Dateieditorfenster, geben den folgenden Code ein und speichern ihn als vampire.py:
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
elif age > 2000:
print('Unlike you, Alice is not an undead, immortal vampire.')
elif age > 100:
print('You are not Alice, grannie.')
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/vampire/ ansehen. Hier habe ich zwei weitere elif-Anweisungen hinzugefügt, damit das Programm die Person auf der Grundlage des Wertes von age mit verschiedenen Meldungen begrüßt. Das Flussdiagramm dafür sehen Sie in Abb. 2–5.
Abb. 2–5Flussdiagramm für das Programm vampire.py mit mehreren elif-Anweisungen
Dabei kommt es jedoch auf die Reihenfolge der elif-Anweisungen an. Um das zu zeigen, ordnen wir sie um, sodass sich ein Fehler ergibt. Da alle restlichen elif-Klauseln verworfen werden, sobald eine wahre Bedingung gefunden wurde, führt eine Umsortierung der Klauseln in vampire.py zu einem Problem. Ändern Sie den Code wie folgt und speichern Sie diese Version als vampire2.py:
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
elif age > 100: 
print('You are not Alice, grannie.')
elif age > 2000:
print('Unlike you, Alice is not an undead, immortal vampire.')
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/vampire2/ ansehen. Nehmen wir nun an, die Variable age enthält vor der Ausführung dieses Codes den Wert 3000, dann könnte man glauben, dass das Programm deshalb den String 'Unlike you, Alice is not an undead, immortal vampire.' ausgibt. Allerdings hat schon die Bedingung age > 100 den Wert True (schließlich ist 3000 größer als 100) (
). Daher wird der String 'You are not Alice, grannie.' ausgegeben und der Rest der elif-Anweisungen übersprungen. Merken Sie sich, dass höchstens eine Klausel ausgeführt wird und dass es auf die Reihenfolge der elif-Anweisungen ankommt.
Abb. 2–6 zeigt das Flussdiagramm für den vorstehenden Code. Die Rauten für age > 100 und age > 2000 sind hier vertauscht.
Optional können Sie hinter der letzten elif-Anweisung noch eine else-Anweisung einfügen. In diesem Fall wird garantiert genau eine Klausel ausgeführt. Wenn die Bedingungen in sämtlichen if- und elif-Anweisungen False sind, wird die else-Klausel ausgeführt. Bauen wir unsere Alice-Beispiel noch ein wenig um, sodass es if-, elif- und else-Klauseln verwendet:
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
else:
print('You are neither Alice nor a little kid.')
Speichern Sie diesen Code als littleKid.py. Das Flussdiagramm sehen Sie in Abb. 2–7, die Ausführung dieses Programms auf https://autbor.com/littlekid/.
In Alltagssprache formuliert, lautet diese Flusssteuerungsstruktur wie folgt: »Wenn die erste Bedingung wahr ist, tu dies. Ist dagegen die zweite Bedingung wahr, tu jenes. Anderenfalls mach etwas anderes.« Wenn Sie alle drei Anweisungen kombinieren, müssen Sie sich an die Regeln für die Reihenfolge halten, damit Sie keine Fehler wie den aus Abb. 2–6 einbauen. Erstens muss es immer genau eine if-Anweisung geben. Jegliche elif-Anweisungen, die Sie brauchen, folgen auf die if-Anweisung. Zweitens: Wenn Sie sicherstellen wollen, dass mindestens eine Klausel ausgeführt wird, müssen Sie die Struktur mit einer else-Anweisung abschließen.
Abb. 2–6Das Flussdiagramm für das Programm vampire2.py. Der durchgestrichene Pfad kann logisch nie erreicht werden, denn wenn der Wert von age größer als 2000 ist, so ist er bereits größer als 100.
Abb. 2–7 Das Flussdiagramm für das Programm littleKid.py
While-Schleifen
Mit einer while-Anweisung können Sie dafür sorgen, dass der Code in der while-Klausel immer wieder ausgeführt wird, solange die Bedingung der Anweisung True ist. Eine while-Anweisung enthält Folgendes:
Das Schlüsselwort while
Eine Bedingung (einen Ausdruck, der zu True oder False ausgewertet wird)
Einen Doppelpunkt
Einen eingerückten Codeblock (die while-Klausel), der in der nächsten Zeile beginnt
Eine while-Anweisung sieht ähnlich aus wie eine if-Anweisung, verhält sich aber ganz anders. Am Ende einer if-Klausel fährt das Programm mit der Ausführung hinter der if-Anweisung fort, wohingegen es am Ende einer while-Klausel zurück zum Beginn der while-Anweisung springt. Die while-Klausel wird oft auch als while-Schleife oder einfach als Schleife bezeichnet.
Zum Vergleich wollen wir uns eine if- und eine while-Anweisung ansehen, die die gleiche Bedingung verwenden und auf der Grundlage dieser Bedingung auch die gleichen Aktionen ausführen. Der Code der if-Anweisung sieht wie folgt aus:
spam = 0
if spam < 5:
print('Hello, world.')
spam = spam + 1
Die while-Anweisung stellt sich folgendermaßen dar:
spam = 0
while spam < 5:
print('Hello, world.')
spam = spam + 1
Die Anweisungen sehen ähnlich aus: Sowohl if als auch while prüfen den Wert von spam und geben eine Meldung aus, wenn dieser Wert kleiner als 5 ist. Aber wenn Sie diese beiden Codefragmente ausführen, geschieht jeweils etwas völlig anderes. Bei der if-Anweisung lautet die Ausgabe schlicht Hello, world., doch bei der while-Anweisung wird dieser Satz fünfmal ausgegeben. Um zu sehen, warum das so ist, schauen Sie sich die Flussdiagramme für diese beiden Codefragmente in Abb. 2–8 und 2–9 an.
Abb. 2–8Das Flussdiagramm für den Code mit der if-Anweisung
Abb. 2–9Das Flussdiagramm für den Code mit der while-Anweisung
Der Code in der if-Anweisung prüft die Bedingung und gibt die Meldung Hello, world. ein einziges Mal aus, wenn die Bedingung wahr ist. Der Code in der while-Schleife dagegen gibt die Meldung fünfmal aus. Danach hält er an, da der Integerwert in spam nach jedem Schleifendurchlauf um 1 erhöht wird. Die Schleife wird also fünfmal ausgeführt, bevor spam < 5 falsch ist.
In einer while-Schleife wird die Bedingung zu Beginn jeder Iteration (also vor jedem Durchlauf durch die Schleife) geprüft. Ist sie True, so wird die Klausel ausgeführt. Danach wird die Bedingung erneut geprüft. Wenn die Bedingung zum ersten Mal False ist, wird die while-Klausel übersprungen.
Das folgende kleine Beispielprogramm fordert Sie immer wieder auf, »your name« einzugeben, bis Sie tatsächlich wortwörtlich »your name« eingeben (und nicht etwa Ihren Namen). Öffnen Sie mit File > New ein neues Dateieditorfenster, geben Sie den folgenden Code ein und speichern Sie die Datei als yourName.py:
name = '' 
while name != 'your name': 
print('Please type your name.')
name = input() 
print('Thank you!') 
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/yourname/ ansehen. Zu Anfang setzt es die Variable name auf einen leeren String (
), damit die Bedingung name != 'your name' zu True ausgewertet wird und das Programm mit der Ausführung der while-Klausel beginnt (
).
Der Code in dieser Klausel fordert den Benutzer auf, seinen Namen einzugeben, und diese Eingabe wird der Variablen name zugewiesen (
). Da dies die letzte Zeile des Blocks ist, kehrt die Ausführung wieder zum Anfang der while-Schleife zurück, wo die Bedingung erneut ausgewertet wird. Wenn der Wert in name ungleich dem String 'your name' ist, so ist die Bedingung True, weshalb die while-Klausel erneut ausgeführt wird.
Wenn der Benutzer aber your name eingibt, lautet die Bedingung 'your name' != 'your name', was False ist. Daher wird die while-Schleife nicht erneut ausgeführt, sondern übersprungen, sodass die Ausführung mit dem Rest des Programms fortgesetzt wird (
). Abbildung 2–10 zeigt das Flussdiagramm des Programms yourName.py.
Abb. 2–10 Das Flussdiagramm für das Programm yourName.py
Schauen wir uns yourName.py nun in Aktion an. Drücken Sie
, um das Programm auszuführen, und geben Sie mehrere Male irgendetwas anderes als your name ein, bevor Sie dem Programm schließlich das geben, was es haben will.
Please type your name.
Al
Please type your name.
Albert
Please type your name.
%#@#%*(^&!!!
Please type your name.
your name
Thank you!
Wenn Sie niemals your name eingeben, ist die Bedingung der while-Schleife niemals False. Das Programm wird Sie dann bis in alle Ewigkeit zur Eingabe auffordern. Der Aufruf von input() stellt für den Benutzer die Möglichkeit bereit, den richtigen String einzugeben, damit das Programm aus der Schleife herauskommt. In einem Programm kann es jedoch durchaus vorkommen, dass sich die Bedingung niemals ändert, was ein ziemliches Problem darstellen kann. Sehen wir uns daher an, wie Sie aus einer while-Schleife ausbrechen können.
Break-Anweisungen
Es gibt eine Abkürzung, um vorzeitig aus einer while-Schleife auszubrechen. Wenn das Programm bei der Ausführung eine break-Anweisung erreicht, verlässt es die while-Klausel sofort. Im Code besteht eine break-Anweisung lediglich aus dem Schlüsselwort break.
Ziemlich einfach, oder? Das folgende Programm macht das Gleiche wie das vorherige, enthält zusätzlich aber eine break-Anweisung, um aus der Schleife ausbrechen zu können. Geben Sie den folgenden Code ein und speichern Sie die Datei als yourName2.py:
while True: 
print('Please type your name.')
name = input() 
if name == 'your name': 
break 
print('Thank you!') 
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/yourname2/ ansehen. Die erste Zeile (
) ruft eine Endlosschleife hervor – eine while-Schleife, deren Bedingung stets True ist (denn schließlich wird der Ausdruck True immer zu dem Wert True ausgewertet). Tritt das Programm erst einmal in die Schleife ein, kann es sie erst dann verlassen, wenn eine break-Anweisung ausgeführt wird. (Eine Endlosschleife, die niemals verlassen wird, ist ein häufig auftretender Bug.)
Wie zuvor fordert das Programm den Benutzer dazu auf, your name einzugeben (
). Diesmal aber wird innerhalb der while-Schleife eine if-Anweisung ausgeführt (
), um zu prüfen, ob name gleich 'your name' ist. Ist diese Bedingung erfüllt, so wird die break-Anweisung ausgeführt (
), sodass das Programm die Schleife verlässt und zu print('Thank you!') übergeht (
). Anderenfalls wird die if-Klausel mit der break-Anweisung übersprungen, sodass die Programmausführung das Ende der while-Schleife erreicht. Dadurch springt sie wieder zum Anfang der while-Anweisung, wo die Bedingung überprüft wird (
). Da es sich bei dieser Bedingung einfach um den booleschen Wert True handelt, beginnt die Schleife erneut, sodass der Benutzer wieder aufgefordert wird, your name einzugeben. Das Flussdiagramm für dieses Programm sehen Sie in Abb. 2–11.
Führen Sie yourName2.py aus und geben Sie den gleichen Text ein wie bei yourName.py. Die neue Version des Programms reagiert auf die gleiche Weise wie die ursprüngliche.
Abb. 2–11Das Flussdiagramm für das Programm yourName2.py mit einer Endlosschleife. Der mit X markierte Pfad wird nie erreicht, da die Schleifenbedingung stets True ist.
Continue-Anweisungen
Ebenso wie break- werden auch continue-Anweisungen innerhalb von Schleifen verwendet. Wird bei der Ausführung eine solche Anweisung erreicht, so erfolgt ein Rücksprung zum Anfang der Schleife, wo die Schleifenbedingung erneut ausgewertet wird. (Dies geschieht auch, wenn die Ausführung am Ende der Schleife ankommt.)
Gefangen in einer Endlosschleife?
Wenn Sie ein Programm ausführen und es aufgrund eines Bugs in einer Endlosschleife hängen bleibt, drücken Sie
+
oder wählen Sie im IDLE-Menü Shell > Restart Shell. Dadurch wird ein KeyboardInterrupt-Fehler an das Programm gesendet und es bricht sofort ab. Um das auszuprobieren, schreiben Sie im Dateieditor eine einfache Endlosschleife und speichern das Programm als infiniteloop.py:
while True:
print('Hello, world!')
Wenn Sie dieses Programm ausführen, gibt es Hello, world! auf dem Bildschirm aus und will nicht mehr damit aufhören, da die Bedingung der while-Anweisung stets True ist. Die Tastenkombination
+
ist auch dann praktisch, wenn ein Programm nicht in einer Endlosschleife gefangen ist, Sie es aber aus einem anderen Grund sofort abbrechen wollen.
Im Folgenden verwenden wir continue für ein Programm, das Sie nach Ihrem Namen und Ihrem Passwort fragt. Geben Sie den folgenden Code in ein neues Dateieditorfenster ein und speichern Sie das Programm als swordfish.py:
while True:
print('Who are you?')
name = input()
if name != 'Joe': 
continue 
print('Hello, Joe. What is the password? (It is a fish.)')
password = input() 
if password == 'swordfish':
break 
print('Access granted.') 
Wenn der Benutzer einen anderen Namen als Joe eingibt (
), sorgt die continue-Anweisung (
) dafür, dass die Programmausführung zum Beginn der Schleife zurückspringt. Nach der Auswertung der Bedingung wird stets die Schleife ausgeführt, da es sich bei dieser Bedingung einfach um den Wert True handelt. Hat die Ausführung erst einmal die erste if-Anweisung überwunden, wird der Benutzer nach dem Passwort gefragt (
). Gibt der Benutzer swordfish ein, wird die break-Anweisung ausgeführt (
), sodass das Programm die while-Schleife verlässt und die Meldung Access granted. ausgibt (
). Anderenfalls wird die Ausführung bis zum Ende der while-Schleife fortgesetzt, von wo aus sie zum Beginn der Schleife zurückspringt. Das können Sie in dem Flussdiagramm in Abb. 2–12 ablesen.
Abb. 2–12Das Flussdiagramm für das Programm swordfish.py. Der mit X markierte Pfad wird nie erreicht, da die Schleifenbedingung stets True ist.
»Truthy«- und »Falsey«-Werte
Es gibt auch einige Werte anderer Datentypen, die in Bedingungen als gleichwertig mit True und False angesehen werden. In Bedingungen gelten 0, 0.0 und '' (ein leerer String) als False, alle anderen Werte dagegen als True. Betrachten Sie beispielsweise das folgende Programm:
name = ''
while not name: 
print('Enter your name:')
name = input()
print('How many guests will you have?')
numOfGuests = int(input())
if numOfGuests: 
print('Be sure to have enough room for all your guests.') 
print('Done')
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/howmanyguests/ ansehen. Wenn der Benutzer einen leeren String für name eingibt, dann ist die Bedingung der while-Anweisung True (
), sodass das Programm weiterhin nach dem Namen fragt. Ist der Wert von numOfGuests ungleich 0 (
), wird die Bedingung als True aufgefasst, sodass das Programm einen Hinweis für den Benutzer ausgibt (
).
Sie könnten auch not name != '' statt not name und numOfGuests != 0 statt numOfGuests schreiben, aber mit den sogenannten Truthy- und Falsey-Werten wird der Code übersichtlicher.
Führen Sie das Programm swordfish.py aus und machen Sie einige Eingaben. Erst wenn Sie sich als Joe vorstellen, werden Sie nach dem Passwort gefragt. Nachdem Sie das richtige Passwort eingegeben haben, endet das Programm.
Who are you?
I'm fine, thanks. Who are you?
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
Mary
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
swordfish
Access granted.
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/hellojoe/ ansehen.
For-Schleifen und die Funktion range()
While-Schleifen werden durchlaufen, solange die Bedingung True ist. Was aber machen Sie, wenn Sie einen Codeblock eine bestimmte Anzahl von Malen ausführen wollen? Das können Sie mit der Anweisung for und der Funktion range() erreichen.
Eine for-Anweisung hat die Form for i in range(5) und weist die folgenden Bestandteile auf:
Das Schlüsselwort for
Einen Variablennamen
Das Schlüsselwort in
Einen Aufruf der Methode range(), wobei bis zu drei Integer übergeben werden können
Einen Doppelpunkt
Einen eingerückten Codeblock (die for-Klausel), der in der nächsten Zeile beginnt
Um uns eine for-Schleife in Aktion anzusehen, schreiben wir das Programm five-Times.py:
print('My name is')
for i in range(5):
print('Jimmy Five Times (' + str(i) + ')')
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/fivetimesfor/ ansehen. Der Code in der for-Klausel wird fünfmal ausgeführt. Beim ersten Mal hat die Variable i den Wert 0. Der Aufruf von print() in der Klausel gibt daher Jimmy Five Times (0) aus. Nach einem kompletten Durchlauf durch den Code in der Klausel wird die Ausführung wieder am Anfang der Schleife fortgesetzt und i um 1 erhöht. Die Funktion range(5) ruft fünf Iterationen durch die Klausel hervor, da i nacheinander auf 0, 1, 2, 3 und 4 gesetzt wird. Die Variable wird erhöht, bis die nächste Erhöhung den an range() übergebenen Wert erreichen würde. Das Flussdiagramm für fiveTimes.py sehen Sie in Abb. 2–13.
Abb. 2–13 Das Flussdiagramm für fiveTimes.py
Wenn Sie das Programm ausführen, gibt es fünfmal Jimmy Five Times jeweils gefolgt von dem Wert von i aus, bevor es die Schleife verlässt.
My name is
Jimmy Five Times (0)
Jimmy Five Times (1)
Jimmy Five Times (2)
Jimmy Five Times (3)
Jimmy Five Times (4)
Hinweis
Auch in for-Schleifen können Sie die Anweisungen break und continue verwenden. Dabei sorgt eine continue-Anweisung dafür, dass das Programm mit dem nächsten Wert des Schleifenzählers fortfährt, als wäre die Ausführung am Ende der Schleife angekommen und zum Start zurückgesprungen. Break- und continue-Anweisungen lassen sich ausschließlich in while- und for-Schleifen einsetzen. Wenn Sie versuchen, sie an anderen Stellen zu verwenden, gibt Python eine Fehlermeldung aus.
Unser nächstes Beispiel für eine for-Schleife geht auf eine Geschichte über den Mathematiker Carl Friedrich Gauß zurück. Als er ein kleiner Junge war, wollte sein Lehrer die Klasse beschäftigt halten und wies die Schüler daher an, alle Zahlen von 1 bis 100 zu addieren. Der kleine Gauß kam aber auf einen Trick, mit dem er das Ergebnis in wenigen Sekunden berechnen konnte. In Python brauchen Sie diesen Trick gar nicht, sondern können die Arbeit einfach in einer for-Schleife erledigen lassen:
total = 0 
for num in range(101): 
total = total + num 
print(total) 
Das Ergebnis lautet 5050. Zu Anfang des Programms wird die Variable total auf 0 gesetzt (
). Die for-Schleife (
) führt dann 100 Mal total = total + num (
) aus. Wenn die Schleife alle 100 Iterationen durchlaufen hat, sind alle ganzen Zahlen von 1 bis 100 zu total addiert worden. Jetzt wird der Wert von total auf dem Bildschirm ausgegeben (
). Selbst auf dem langsamsten Computer nimmt dieses Programm weniger als eine Sekunde in Anspruch.
(Der kleine Gauß hatte übrigens festgestellt, dass die Zahlen von 1 bis 100 aus 50 Paaren mit der Summe 101 bestanden: 1 + 100, 2 + 99, 3 + 98 usw. bis 50 + 51. Daher musste er nur noch 101 × 50 = 5050 rechnen. Schlaues Bürschchen!)
Eine gleichwertige while-Schleife
Es ist möglich, eine while-Schleife zu konstruieren, die genau das Gleiche macht wie eine for-Schleife. Die for-Schleife stellt nur eine knappere Schreibweise dar. Die folgende Variante von fiveTimes.py verwendet eine while-statt der for-Schleife:
print('My name is')
i = 0
while i < 5:
print('Jimmy Five Times (' + str(i) + ')')
i = i + 1
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/fivetimeswhile/ ansehen. Wenn Sie es ausprobieren, erhalten Sie die gleiche Ausgabe wie bei dem Programm fiveTimes.py mit der for-Schleife.
Das Anfangs-, End- und Schrittargument für range()
Manche Funktionen können mit mehreren, durch Kommata getrennten Argumenten aufgerufen werden. Unter anderem ist das auch bei range() möglich. Damit können Sie dafür sorgen, dass die Funktion beliebige Intervalle durchläuft, auch solche, die nicht mit 0 beginnen:
for i in range(12, 16):
print(i)
Das erste Argument gibt den Wert an, mit dem die Variable der for-Schleife beginnt, das zweite die Zahl, bis zu der die Inkrementierung durchgeführt wird (ohne sie zu erreichen):
12
13
14
15
Die Funktion range() kann auch mit drei Argumenten aufgerufen werden. Dabei sind die ersten beiden wie gehabt der Anfangs- und der Endwert, während der dritte die Schrittweite angibt, also den Wert, um den die Variable nach jeder Iteration erhöht wird.
for i in range(0, 10, 2):
print(i)
Wenn Sie also range(0, 10, 2) aufrufen, wird in Abständen von 2 von 0 bis 8 gezählt:
0
2
4
6
8
Was die Reihenfolge der Zahlen für die for-Schleifen angeht, ist die Funktion range() sehr flexibel. Sie können auch eine negative Zahl als Schrittweite angeben, damit die for-Schleife abwärts zählt statt aufwärts.
for i in range(5, -1, -1):
print(i)
Diese for-Schleife hat die folgende Ausgabe:
5
4
3
2
1
0
Module importieren
Alle Python-Programme können einen grundlegenden Satz von eingebauten oder integrierten Funktionen nutzen, beispielsweise print(), input() und len(), die Sie schon kennengelernt haben. Darüber hinaus wird Python mit einem Satz von Modulen geliefert, der als Standardbibliothek bezeichnet wird. Jedes Modul ist ein Python-Programm mit mehreren thematisch verwandten Funktionen, die Sie in Ihr Programm einbetten können. Beispielsweise verfügt das Modul math über mathematische Funktionen, das Modul random über Funktionen für Zufallszahlen usw.
Bevor Sie die Funktionen eines Moduls nutzen können, müssen Sie das Modul mit der Anweisung import importieren. Diese Anweisung hat die folgenden Bestandteile:
Das Schlüsselwort import
Den Namen des Moduls
Optional weitere Modulnamen, die durch Kommata voneinander getrennt sind
Nachdem Sie ein Modul importiert haben, können Sie alle seine Funktionen nutzen. Das wollen wir anhand des Moduls random ausprobieren, das uns Zugriff auf die Funktion random.randint() gibt.
Geben Sie den folgenden Code in den Dateieditor ein und speichern Sie ihn als printRandom.py:
import random
for i in range(5):
print(random.randint(1, 10))
Überschreiben Sie keine Modulnamen!
Wenn Sie Ihre Python-Skripte speichern, müssen Sie darauf achten, dass Sie ihnen nicht die Namen von Python-Modulen wie random.py, sys.py, os.py oder math.py geben. Sollten Sie versehentlich eines Ihrer Programme etwa random.py nennen und dann in einem anderen Programm die Anweisung import random verwenden, so wird statt des Python-Moduls random Ihre Datei random.py importiert. Das kann zu einer Fehlermeldung wie AttributeError: module 'random' has no attribute 'randint' führen, da Ihre Datei nicht über die Funktionen verfügt, die sich in dem echten Modul random befinden. Verwenden Sie auch keine Namen von eingebauten Python-Funktionen wie print()oder input().
Solche Probleme sind zwar selten, doch wenn sie auftreten, lassen sie sich nur schwer finden. Mit zunehmender Programmiererfahrung werden Sie die Namen der Standardmodule und -funktionen von Python besser kennen und dadurch noch seltener in solche Schwierigkeiten kommen.
Wenn Sie dieses Programm ausführen, erhalten Sie eine Ausgabe wie die folgende:
4
1
8
4
1
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/printrandom/ ansehen. Der Aufruf der Funktion random.randint() wird zu einem zufälligen Integerwert ausgewertet, der zwischen den beiden übergebenen Integern liegt. Da randint() zum Modul random gehört, müssen Sie dem Funktionsnamen random. voranstellen, um Python anzuweisen, in diesem Modul nach der Funktion zu suchen.
Das folgende Beispiel zeigt eine import-Anweisung, die gleich vier verschiedene Module importiert:
import random, sys, os, math
Danach können Sie alle Funktionen dieser vier Module nutzen. Mehr darüber erfahren Sie weiter hinten in diesem Buch.
From-import-Anweisungen
Eine alternative Form der import-Anweisung besteht aus dem Schlüsselwort from gefolgt vom Modulnamen, dem Schlüsselwort import und einem Sternchen, also z. B. from random import *.
Bei dieser Form müssen Sie Aufrufen der Funktionen aus random das Präfix random. nicht mehr voranstellen. Allerdings ist der Code leichter verständlich, wenn Sie den vollständigen Namen verwenden, weshalb es besser ist, sich an die übliche Form der import-Anweisung zu halten.
Programme mit sys.exit() vorzeitig beenden
Der letzte Aspekt der Flusssteuerung, mit dem wir uns hier beschäftigen, ist die Beendigung eines Programms. Wenn die Ausführung am Ende der Anweisungen angelangt ist, wird das Programm automatisch beendet, allerdings können Sie durch einen Aufruf der Funktion sys.exit() auch dafür sorgen, dass das Programm vor der letzten Anweisung beendet wird. Da diese Funktion zum Modul sys gehört, müssen Sie dieses zunächst in Ihr Programm importieren, bevor Sie die Funktion nutzen können.
Geben Sie in einem neuen Dateieditorfenster den folgenden Code ein und speichern Sie ihn als exitExample.py:
import sys
while True:
print('Type exit to exit.')
response = input()
if response == 'exit':
sys.exit()
print('You typed ' + response + '.')
Führen Sie das Programm in IDLE aus. Es enthält eine Endlosschleife ohne break-Anweisung. Es wird nur beendet, wenn die Ausführung den Aufruf von sys.exit() erreicht. Wenn response gleich exit ist, wird die Zeile mit sys.exit() ausgeführt. Da die Variable response aber durch die Funktion input() festgelegt wird, muss der Benutzer exit eingeben, um das Programm zu beenden.
Ein kurzes Programm: Zahlen raten
Die Beispiele, die ich Ihnen bis jetzt gezeigt habe, mögen vielleicht ganz nützlich sein, um einfache Prinzipien zu veranschaulichen, aber im Folgenden wollen wir uns ansehen, wie wir das Gelernte in einem Programm anwenden, das auch wirklich etwas tut. Dazu schreiben wir ein einfaches Zahlenratespiel. Wenn Sie das Programm ausführen, erhalten Sie eine Ausgabe wie die folgende:
I am thinking of a number between 1 and 20.
Take a guess.
10
Your guess is too low.
Take a guess.
15
Your guess is too low.
Take a guess.
17
Your guess is too high.
Take a guess.
16
Good job! You guessed my number in 4 guesses!
Geben Sie den folgenden Quellcode in den Dateieditor ein und speichern Sie ihn als guessTheNumber.py:
# Dies ist ein Zahlenratespiel.
import random
secretNumber = random.randint(1, 20)
print('I am thinking of a number between 1 and 20.')
# Fordert den Spieler sechsmal zum Raten der Zahl auf.
for guessesTaken in range(1, 7):
print('Take a guess.')
guess = int(input())
if guess < secretNumber:
print('Your guess is too low.')
elif guess > secretNumber:
print('Your guess is too high.')
else:
break # Diese Bedingung tritt ein, wenn die Zahl erraten wurde!
if guess == secretNumber:
print('Good job! You guessed my number in ' + str(guessesTaken) + '
guesses!')
else:
print('Nope. The number I was thinking of was ' + str(secretNumber))
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/guessthenumber/ ansehen. Betrachten wir den Code nun Zeile für Zeile. Als Erstes haben wir Folgendes:
# Dies ist ein Zahlenratespiel.
import random
secretNumber = random.randint(1, 20)
Der Kommentar am Anfang des Codes erklärt zunächst einmal, was das Programm macht. Danach importiert das Programm das Modul random, damit es mit der Funktion random.randint() die zu ratende Zahl generieren kann. Der Rückgabewert, ein zufälliger Integer zwischen 1 und 20, wird in der Variablen secretNumber gespeichert.
print('I am thinking of a number between 1 and 20.')
# Fordert den Spieler sechsmal zum Raten der Zahl auf.
for guessesTaken in range(1, 7):
print('Take a guess.')
guess = int(input())
Das Programm teilt dem Spieler nun mit, dass es sich eine Geheimzahl ausgedacht hat und den Spieler sechsmal raten lässt. Der Code, der den Spieler einen Rateversuch eingeben lässt und die Zahl anschließend überprüft, steht in einer for-Schleife, die maximal sechsmal ausgeführt wird. In der Schleife wird als Erstes die Eingabe des Spielers entgegengenommen. Da input() einen String zurückgibt, wird dieser Rückgabewert sofort an die Funktion int() übergeben, die ihn in einen Integerwert umwandelt. Dieser wird schließlich in der Variablen guess abgelegt.
if guess < secretNumber:
print('Your guess is too low.')
elif guess > secretNumber:
print('Your guess is too high.')
Diese wenigen Codezeilen prüfen, ob die geratene Zahl größer oder kleiner als die Geheimzahl ist. In beiden Fällen wird auf dem Bildschirm ein entsprechender Hinweis ausgegeben.
else:
break # Diese Bedingung tritt ein, wenn die Zahl erraten wurde!
Ist die geratene Zahl weder kleiner noch größer als die Geheimzahl, so muss sie gleich dieser Zahl sein. In diesem Fall wird die for-Schleife abgebrochen.
if guess == secretNumber:
print('Good job! You guessed my number in ' + str(guessesTaken) + '
guesses!')
else:
print('Nope. The number I was thinking of was ' + str(secretNumber))
Nach der for-Schleife prüft die vorstehende if...else-Anweisung, ob der Spieler die Zahl erraten hat oder nicht, und gibt eine entsprechende Meldung auf dem Bildschirm aus. In beiden Fällen zeigt das Programm eine Variable mit einem Integerwert an (guessesTaken bzw. secretNumber). Da diese Integerwerte mit Strings verkettet werden müssen, werden die Variablen an die Funktion str() übergeben, die die Stringversionen der Integer zurückgibt. Jetzt können die Strings mit dem Operator + verkettet und schließlich an print() übergeben werden.
Ein kurzes Programm: Stein, Schere, Papier
Mit den bisher erlernten Programmiertechniken wollen wir nun das einfache Spiel »Stein, Schere, Papier« umsetzen. Die Ausgabe soll wie folgt aussehen:
ROCK, PAPER, SCISSORS
0 Wins, 0 Losses, 0 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
p
PAPER versus...
PAPER
It is a tie!
0 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
s
SCISSORS versus...
PAPER
You win!
1 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
q
Geben Sie den folgenden Quellcode in den Dateieditor ein und speichern Sie ihn als rpsGame.py:
import random, sys
print('ROCK, PAPER, SCISSORS')
# Diese Variablen halten die Anzahl der Siege, Niederlagen und
# Unentschieden fest.
wins = 0
losses = 0
ties = 0
while True: # Hauptschleife des Spiels.
print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
while True: # Schleife für die Eingabe des Spielers.
print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
playerMove = input()
if playerMove == 'q':
sys.exit() # Beendet das Programm.
if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
break # Beendet die Eingabeschleife.
print('Type one of r, p, s, or q.')
# Zeigt die Wahl des Spielers an:
if playerMove == 'r':
print('ROCK versus...')
elif playerMove == 'p':
print('PAPER versus...')
elif playerMove == 's':
print('SCISSORS versus...')
# Zeigt die Wahl des Computers an:
randomNumber = random.randint(1, 3)
if randomNumber == 1:
computerMove = 'r'
print('ROCK')
elif randomNumber == 2:
computerMove = 'p'
print('PAPER')
elif randomNumber == 3:
computerMove = 's'
print('SCISSORS')
# Zeigt und speichert Sieg/Niederlage/Unentschieden:
if playerMove == computerMove:
print('It is a tie!')
ties = ties + 1
elif playerMove == 'r' and computerMove == 's':
print('You win!')
wins = wins + 1
elif playerMove == 'p' and computerMove == 'r':
print('You win!')
wins = wins + 1
elif playerMove == 's' and computerMove == 'p':
print('You win!')
wins = wins + 1
elif playerMove == 'r' and computerMove == 'p':
print('You lose!')
losses = losses + 1
elif playerMove == 'p' and computerMove == 's':
print('You lose!')
losses = losses + 1
elif playerMove == 's' and computerMove == 'r':
print('You lose!')
losses = losses + 1
Sehen wir uns diesen Code nun Zeile für Zeile an:
import random, sys
print('ROCK, PAPER, SCISSORS')
# Diese Variablen halten die Anzahl der Siege, Niederlagen und
# Unentschieden fest.
wins = 0
losses = 0
ties = 0
Als Erstes importieren wir hier die Module random und sys, damit das Programm die Funktionen random.randint() und sys.exit() aufrufen kann. Außerdem richten wir drei Variablen ein, damit sich das Programm merken kann, wie oft der Spieler gewonnen, verloren oder ein Unentschieden erzielt hat.
while True: # Hauptschleife des Spiels.
print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
while True: # Schleife für die Eingabe des Spielers.
print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
playerMove = input()
if playerMove == 'q':
sys.exit() # Beendet das Programm.
if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
break # Beendet die Eingabeschleife.
print('Type one of r, p, s, or q.')
Hier ist eine while-Schleife in einer anderen while-Schleife verschachtelt. Bei der äußeren handelt es sich um die Hauptschleife des Spiels. Jeder Durchlauf durch diese Schleife stellt eine einzelne Runde des Spiels dar. Die innere Schleife fordert den Spieler zu einer Eingabe auf und wird so lange wiederholt, bis der Spieler r, p, s oder q eingegeben hat, wobei r, p und s für Stein (rock), Papier (paper) und Schere (scissors) stehen und q bedeutet, dass der Benutzer das Spiel beenden will. In letzterem Fall wird sys.exit() aufgerufen und das Programm beendet. Gibt der Spieler r, p oder s ein, verlässt das Programm die Schleife. Anderenfalls fordert es ihn auf, r, p, s oder q einzugeben und kehrt wieder zum Anfang der Schleife zurück.
# Zeigt die Wahl des Spielers an:
if playerMove == 'r':
print('ROCK versus...')
elif playerMove == 'p':
print('PAPER versus...')
elif playerMove == 's':
print('SCISSORS versus...')
Damit wird die Auswahl des Spielers angezeigt.
# Zeigt die Wahl des Computers an:
randomNumber = random.randint(1, 3)
if randomNumber == 1:
computerMove = 'r'
print('ROCK')
elif randomNumber == 2:
computerMove = 'p'
print('PAPER')
elif randomNumber == 3:
computerMove = 's'
print('SCISSORS')
Danach wird der Zug des Computers zufällig ausgewählt. Allerdings gibt random.randint() nur Zahlen aus. Daher wird der zurückgegebene Integerwert 1, 2 oder 3 in der Variablen randomNumber abgelegt und dann auf der Grundlage dieses Wertes der String 'r', 'p' oder 's' in computerMove gespeichert. Anschließend wird die Wahl des Computers angezeigt.
# Zeigt Sieg/Niederlage/Unentschieden an und speichert das Ergebnis:
if playerMove == computerMove:
print('It is a tie!')
ties = ties + 1
elif playerMove == 'r' and computerMove == 's':
print('You win!')
wins = wins + 1
elif playerMove == 'p' and computerMove == 'r':
print('You win!')
wins = wins + 1
elif playerMove == 's' and computerMove == 'p':
print('You win!')
wins = wins + 1
elif playerMove == 'r' and computerMove == 'p':
print('You lose!')
losses = losses + 1
elif playerMove == 'p' and computerMove == 's':
print('You lose!')
losses = losses + 1
elif playerMove == 's' and computerMove == 'r':
print('You lose!')
losses = losses + 1
Abschließend vergleicht das Programm die Strings in playerMove und computerMove und zeigt das Ergebnis auf dem Bildschirm an. Außerdem erhöht es je nach Ausgang den Wert der Variablen wins, losses oder ties. Wenn die Ausführung am Ende angekommen ist, springt sie wieder zurück zum Anfang der Hauptschleife, sodass eine neue Runde beginnen kann.
Zusammenfassung
Mithilfe von Ausdrücken, die zu True oder False ausgewertet werden (sogenannten Bedingungen), können Sie Programme schreiben, die entscheiden, welcher Code ausgeführt und welcher übersprungen werden soll. Es ist auch möglich, Code in einer Schleife wiederholt auszuführen, während eine bestimmte Bedingung True ist. Wenn Sie eine Schleife abbrechen oder innerhalb einer Schleife zum Anfang zurückkehren müssen, können Sie die Anweisung break bzw. continue nutzen.
Diese Flusssteuerungsanweisungen erlauben es Ihnen, viel intelligentere Programme zu schreiben. Es gibt jedoch noch eine andere Form von Flusssteuerung, indem Sie eigene Funktionen schreiben. Sie bilden das Thema des nächsten Kapitels.
Wiederholungsfragen
1 Welche beiden Werte gibt es für den booleschen Datentyp? Wie werden sie geschrieben?
2 Wie heißen die drei booleschen Operatoren?
3 Schreiben Sie die Wahrheitswertetafeln aller booleschen Operatoren auf (also die Tabellen aller möglichen Kombinationen der booleschen Werte für diese Operatoren und das jeweilige Ergebnis).
4 Wie lauten die Ergebnisse der folgenden Ausdrücke?(5 > 4) and (3 == 5)not (5 > 4)(5 > 4) or (3 == 5)not ((5 > 4) or (3 == 5))(True and True) and (True == False)(not False) or (not True)
5 Wie heißen die sechs Vergleichsoperatoren?
6 Was ist der Unterschied zwischen dem Gleichheits- und dem Zuweisungsoperator?
7 Was ist eine Bedingung? Wann wird sie verwendet?
8 Bezeichnen Sie die drei Blöcke in dem folgenden Code:spam = 0if spam == 10:print('eggs')if spam > 5:print('bacon')else:print('ham')print('spam')print('spam')
9 Schreiben Sie Code, der Hello ausgibt, wenn in spam der Wert 1 gespeichert ist, Howdy, wenn spam den Wert 2 hat, und Greetings! bei jedem anderen Wert von spam.
10 Welche Tastenkombination können Sie drücken, wenn ein Programm in einer Endlosschleife gefangen ist?
11 Was ist der Unterschied zwischen break und continue?
12 Was ist der Unterschied zwischen range(10), range(0, 10) und range(0, 10, 1) in einer for-Schleife?
13 Schreiben Sie ein kurzes Programm, das die Zahlen von 1 bis 10 mithilfe einer for-Schleife ausgibt. Schreiben Sie dann ein Programm, das die gleiche Aufgabe mithilfe einer while-Schleife erfüllt.
14 Nehmen Sie an, Sie haben ein Modul namens spam, in dem sich die Funktion bacon() befindet. Wie rufen Sie bacon() auf, nachdem Sie spam importiert haben?
Zusatzpunkt: Schlagen Sie die Funktionen round() und abs() im Internet nach und finden Sie heraus, was sie tun. Experimentieren Sie in der interaktiven Shell damit.
3
Funktionen
In den vorhergehenden Kapiteln haben Sie bereits die Funktionen print(), input() und len() kennengelernt. Python bietet noch weitere integrierte Funktionen wie diese, aber Sie können auch Ihre eigenen schreiben. Eine Funktion ist ein Miniprogramm innerhalb eines Programms.
Um besser zu verstehen, wie Funktionen aufgebaut sind und was sie tun, wollen wir eine erstellen. Geben Sie das folgende Programm im Dateieditor ein und speichern Sie es als helloFunc.py:
def hello(): 
print('Howdy!') 
print('Howdy!!!')
print('Hello there.')
hello() 
hello()
hello()
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/hellofunc/ ansehen. Die erste Zeile enthält die Anweisung def (
), die eine Funktion namens hello() definiert. Der Code in dem darauf folgenden Block (
) stellt den Rumpf der Funktion dar. Während der Funktionsdefinition wird er nicht ausgeführt, sondern erst, wenn die Funktion aufgerufen wird.
Die hello()-Zeilen im Anschluss an die Funktionsdefinition (
) sind Funktionsaufrufe. Im Code wird ein solcher Aufruf in Form des Funktionsnamens gefolgt von den Klammern geschrieben, wobei in den Klammern Zahlen als Argumente stehen können. Wenn die Programmausführung diese Aufrufe erreicht, springt sie zur ersten Zeile in der Funktion und führt den dort vorhandenen Code aus. Am Ende der Funktion angelangt, kehrt die Ausführung wieder zu der Zeile zurück, in der die Funktion aufgerufen wurde. Der Code wird dann wie gehabt weiter abgearbeitet.
Da das Programm die Funktion hello() dreimal aufruft, wird der Code von hello() auch dreimal ausgeführt. Wenn Sie das Programm starten, sehen Sie folgende Ausgabe:
Howdy!
Howdy!!!
Hello there.
Howdy!
Howdy!!!
Hello there.
Howdy!
Howdy!!!
Hello there.
Ein wichtiger Zweck von Funktionen besteht darin, Code, der mehrfach ausgeführt wird, an einer Stelle zentral vorzuhalten. Ohne Funktionen müssten Sie den Code überall dort, wo er benötigt wird, komplett einfügen. Das Programm sähe dann wie folgt aus:
print('Howdy!')
print('Howdy!!!')
print('Hello there.')
print('Howdy!')
print('Howdy!!!')
print('Hello there.')
print('Howdy!')
print('Howdy!!!')
print('Hello there.')
Eine solche Duplizierung von Code sollten Sie in jedem Fall vermeiden, denn wenn Sie Ihren Code irgendwann ändern – zum Beispiel, um einen Fehler zu korrigieren –, müssten Sie ihn sonst an jeder einzelnen Stelle anpassen, an der Sie ihn eingefügt haben.
Je mehr Programmiererfahrung Sie haben, umso häufiger werden Sie duplizierten oder kopierten Code entfernen können. Dadurch werden Ihre Programme kürzer, besser lesbar und leichter zu ändern.
Def-Anweisungen mit Parametern
Beim Aufruf von Funktionen wie print() und len() übergeben Sie Werte, sogenannte Argumente, indem Sie sie in die Klammern schreiben. Auch Ihre eigenen Funktionen können Sie so definieren, dass sie Argumente annehmen. Geben Sie das folgende Beispiel im Dateieditor ein und speichern Sie es als helloFunc2.py:
def hello(name): 
print('Hello ' + name) 
hello('Alice') 
hello('Bob')
Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:
Hello Alice
Hello Bob
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/hellofunc2/ ansehen. Die Definition der Funktion hello() in diesem Programm schließt den Parameter name ein (
). Ein Parameter ist eine Variable, die Argumente enthält. Wird eine Funktion mit Argumenten aufgerufen, so werden diese Argumente in den Parametern gespeichert. Beim ersten Mal wird die Funktion hello() mit dem Argument 'Alice' aufgerufen (
). Die Programmausführung fährt mit der Funktion fort, wobei die Variable name automatisch auf 'Alice' gesetzt wird. Dieser Text wird in die Ausgabe der Anweisung print() aufgenommen (
).
Sobald die Funktion die Steuerung zurückgibt (wenn die Programmausführung also die Funktion verlässt und normal fortfährt), geht der in dem Parameter gespeicherte Wert jedoch verloren. Wenn Sie in dem vorstehenden Programm hinter hello('Bob') die Anweisung print(name) einfügen, erhalten Sie die Fehlermeldung NameError, da es zu diesem Zeitpunkt keine Variable namens name mehr gibt – sie ist nach dem Abschluss des Funktionsaufrufs hello('Bob') zerstört worden.
Das ähnelt dem Prinzip, dass auch die Variablen im Programm nach Beendigung des Programms vergessen werden. Weiter hinten in diesem Kapitel werde ich im Zusammenhang mit dem lokalen Gültigkeitsbereich von Funktionen noch ausführlicher darauf eingehen.
Terminologie
Die Begriffe definieren, aufrufen, übergeben, Argument und Parameter haben genau festgelegte Bedeutungen. Sehen wir uns das anhand eines Codebeispiels an:
def sayHello(name): 
print('Hello, ' + name)
sayHello('Al') 
Eine Funktion zu definieren bedeutet, sie zu erstellen – auf ähnliche Weise, wie Sie mit einer Zuweisung wie spam = 42 die Variable spam anlegen. Mit der def-Anweisung bei
wird die Funktion sayHello() definiert. In der Zeile sayHello('Al') (
) wird diese Funktion aufgerufen, wobei die Ausführung an den Anfang des Funktionscodes springt. Dieser Funktionsaufruf übergibt außerdem den Stringwert 'Al' an die Funktion. Ein solcher in einem Funktionsaufruf übergebener Wert ist ein Argument. Hier wird das Argument 'Al' der lokalen Variablen name zugewiesen. Variablen, denen Argumente zugewiesen werden, heißen Parameter.
Man kann diese Begriffe leicht verwechseln, es ist aber wichtig, dass Sie den Überblick behalten, denn dadurch ist sichergestellt, dass Sie die Bedeutung des Textes in diesem Kapitel verstehen.
Rückgabewerte und die Anweisung return
Wenn Sie die Funktion len() aufrufen und ihr ein Argument wie 'Hello' übergeben, wird der Funktionsaufruf zur Länge des übergebenen Strings ausgewertet, hier also zu dem Integerwert 5. Der Wert, zu dem ein Funktionsaufruf ausgewertet wird, ist der sogenannte Rückgabewert der Funktion.
Wenn Sie mit def eine eigene Funktion erstellen, können Sie mithilfe der Anweisung return festlegen, was der Rückgabewert sein soll. Eine return-Anweisung weist folgende Bestandteile auf:
Das Schlüsselwort return
Den Wert oder Ausdruck, den die Funktion zurückgeben soll
Wenn Sie in der return-Anweisung einen Ausdruck angeben, ist der Rückgabewert der Wert, zu dem dieser Ausdruck ausgewertet wird. Betrachten Sie als Beispiel das folgende Programm, das je nachdem, welche Zahl als Argument übergeben wird, einen anderen String zurückgibt. Geben Sie den folgenden Code in den Dateieditor ein und speichern Sie ihn als magic8Ball.py:
import random 
def getAnswer(answerNumber): 
if answerNumber == 1: 
return 'It is certain'
elif answerNumber == 2:
return 'It is decidedly so'
elif answerNumber == 3:
return 'Yes'
elif answerNumber == 4:
return 'Reply hazy try again'
elif answerNumber == 5:
return 'Ask again later'
elif answerNumber == 6:
return 'Concentrate and ask again'
elif answerNumber == 7:
return 'My reply is no'
elif answerNumber == 8:
return 'Outlook not so good'
elif answerNumber == 9:
return 'Very doubtful'
r = random.randint(1, 9) 
fortune = getAnswer(r) 
print(fortune) 
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/magic-8ball.py/ ansehen. Zu Beginn importiert Python das Modul random (
). Anschließend wird die Funktion getAnswer() definiert (
). Da dies nur die Definition der Funktion ist, aber kein Aufruf, wird der darin enthaltene Code übersprungen. Die Ausführung fährt mit dem Aufruf der Funktion random.randint() mit den beiden Argumenten 1 und 9 fort (
). Das Ergebnis ist ein Zufallsinteger zwischen 1 und 9 (einschließlich 1 und 9) und wird in der Variablen r gespeichert.
Als Nächstes wird die Funktion getAnswer() mit r als Argument aufgerufen (
). Die Programmausführung springt zum Anfang dieser Funktion (
), wo der Wert r im Parameter answerNumber gespeichert wird. Abhängig von dem Wert dieses Parameters gibt die Funktion nun einen der vielen möglichen Stringwerte zurück. Die Programmausführung kehrt anschließend zu der Zeile im Programm zurück, in der getAnswer() aufgerufen wurde (
). Der zurückgegebene String wird der Variablen fortune zugewiesen, die an den Aufruf der Funktion print() übergeben (
) und damit auf dem Bildschirm ausgegeben wird.
Da Sie Rückgabewerte als Argumente an andere Funktionsaufrufe übergeben können, lassen sich die drei folgenden Zeilen auch abkürzen:
r = random.randint(1, 9)
fortune = getAnswer(r)
print(fortune)
Die folgende einzelne Zeile macht genau das Gleiche:
print(getAnswer(random.randint(1, 9)))
Wie Sie wissen, bestehen Ausdrücke aus Werten und Operatoren. Da ein Funktionsaufruf zu seinem Rückgabewert ausgewertet wird, kann er daher auch in einem Ausdruck verwendet werden.
Der Wert None
In Python gibt es den Wert None, der die Abwesenheit eines Wertes bedeutet. Dies ist der einzige Wert des Datentyps NoneType. (In anderen Programmiersprachen wird er auch als null, nil oder undefined bezeichnet.) Ebenso wie die booleschen Werte True und False muss auch None mit großem Anfangsbuchstaben geschrieben werden.
Dieser Nicht-Wert ist praktisch, wenn Sie etwas speichern müssen, das nicht mit einem echten Wert in einer Variablen verwechselt werden darf. Eine mögliche Anwendung für None ist der Rückgabewert von print(). Diese Funktion zeigt Text auf dem Bildschirm an, muss im Gegensatz zu len() oder input() aber eigentlich nichts zurückgeben. Da aber alle Funktionsaufrufe zu einem Rückgabewert ausgewertet werden, gibt print() pro forma None zurück. Um sich das anzusehen, geben Sie Folgendes in die interaktive Shell ein:
>>> spam = print('Hello!')
Hello!
>>> None == spam
True
Wenn eine Funktionsdefinition keine return-Anweisung hat, hängt Python stillschweigend return None an, ebenso wie eine while- oder for-Schleife implizit mit einer continue-Anweisung endet. Wenn Sie eine return-Anweisung ohne Wert schreiben (also nur das Schlüsselwert return verwenden), wird ebenfalls None zurückgegeben.
Schlüsselwortargumente und print()
Die meisten Argumente werden anhand ihrer Position im Funktionsaufruf identifiziert. Beispielsweise ist random.randint(1, 10) etwas anderes als random.randint(10, 1). Die erste Funktion gibt eine Zufallszahl zwischen 1 und 10 zurück, da das erste Argument die Untergrenze und das zweite die Obergrenze des Intervalls darstellt. Dagegen führt random.randint(10, 1) zu einem Fehler.
Schlüsselwortargumente werden dagegen durch das Schlüsselwort bezeichnet, das ihnen in dem Funktionsaufruf vorangestellt wird. Diese Art von Argumenten wird häufig für optionale Parameter verwendet. Beispielsweise können Sie bei der Funktion print() mit den optionalen Parametern end und sep angeben, was am Ende der Argumente ausgegeben werden soll und was dazwischen (als Trennzeichen).
Betrachten Sie das folgende Beispielprogramm:
print('Hello')
print('World')
Die Ausgabe sieht wie folgt aus:
Hello
World
Die beiden Strings werden auf getrennten Zeilen ausgegeben, da print() am Ende des übergebenen Strings automatisch einen Zeilenumbruch einfügt. Mit dem Schlüsselwortargument end können Sie dieses Verhalten jedoch ändern. Nehmen wir an, wir haben folgendes Programm:
print('Hello', end='')
print('World')
Hier sieht die Ausgabe wie folgt aus:
HelloWorld
Die Ausgabe erscheint auf einer einzigen Zeile, da hinter 'Hello' kein Zeilenumbruch mehr ausgegeben wird, sondern ein leerer String. Das ist nützlich, wenn Sie den automatischen Zeilenumbruch hinter den Funktionsaufrufen von print() aufheben wollen.
Wenn Sie mehrere Stringwerte an print() übergeben, trennt die Funktion sie automatisch durch ein Leerzeichen. Geben Sie beispielsweise Folgendes in die interaktive Shell ein:
>>> print('cats', 'dogs', 'mice')
cats dogs mice
Das Standardtrennzeichen können Sie mit dem Schlüsselwortargument sep ändern. Probieren Sie in der interaktiven Shell Folgendes aus:
>>> print('cats', 'dogs', 'mice', sep=',')
cats,dogs,mice
Auch zu Ihren selbst geschriebenen Funktionen können Sie Schlüsselwortargumente hinzufügen. Dazu müssen Sie sich jedoch mit den Datentypen für Listen und Wörterbücher (Dictionaries) auskennen, die wir in den nächsten beiden Kapiteln behandeln werden. Merken Sie sich zunächst nur, dass einige Funktionen auch über optionale Schlüsselwortargumente verfügen, die Sie beim Aufruf der Funktion angeben können.
Der Aufrufstack
Stellen Sie sich eines dieser Gespräche vor, in denen Sie vom Hundertsten ins Tausendste kommen. Beispielsweise wollen Sie eigentlich über Ihre Freundin Alice sprechen, was Sie an eine Geschichte über Ihren Kollegen Bob erinnert, die Sie aber nicht gleich erzählen können, weil Sie dazu erst einmal etwas über Ihre Cousine Carol berichten müssen. Wenn Sie mit den Hinweisen zu Carol fertig sind, kehren Sie zu der Geschichte über Bob zurück, und danach reden Sie wieder von Alice. Dabei aber werden Sie an Ihren Bruder David erinnert, weshalb Sie eine Bemerkung über ihn einschieben, bevor Sie endlich Ihre Geschichte über Alice abschließen. Ihre Äußerungen folgen dem Muster aus Abb. 3–1, in dem die Gesprächsthemen einen »Stapel« (Stack) bilden und das aktuelle Thema jeweils obenauf liegt.
Abb. 3–1 Der Stack einer Unterhaltung mit vielen Abschweifungen
Ebenso führt auch der Aufruf einer Funktion nicht dazu, dass die Ausführung wie eine Reise ohne Wiederkehr zum Anfang der betreffenden Funktion umgeleitet wird. Python merkt sich, in welcher Zeile die Funktion aufgerufen wurde, sodass die Ausführung nach dem Auftreten einer return-Anweisung dort fortgesetzt werden kann. Ruft die ursprüngliche Funktion weitere Funktionen auf, kehrt die Steuerung nach deren Verarbeitung zunächst zu dem Aufruf dieser verschachtelten Funktionen und erst dann zum Aufruf der ursprünglichen Funktion zurück.
Geben Sie im Dateieditor den folgenden Code ein und speichern Sie ihn als abcdCallstack.py:
def a():
print('a() starts')
b() 
d() 
print('a() returns')
def b():
print('b() starts')
c() 
print('b() returns')
def c():
print('c() starts') 
print('c() returns')
def d():
print('d() starts')
print('d() returns')
a() 
Wenn Sie dieses Programm ausführen, erhalten Sie die folgende Ausgabe:
a() starts
b() starts
c() starts
c() returns
b() returns
d() starts
d() returns
a() returns
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/abcdcallstack/ ansehen. Wird die Funktion a() aufgerufen (
), so ruft sie ihrerseits b() auf (
), die wiederum c() aufruft (
). Die Funktion c() ruft nichts auf, sondern gibt lediglich die Zeilen c() starts (
) und c() returns aus, bevor die Steuerung zu der Zeile in b() zurückspringt, in der c() aufgerufen wurde (
). Dort angekommen, springt die Steuerung zu der Zeile in a() zurück, in der b() aufgerufen wurde (
). Die Ausführung geht jetzt mit der nächsten Zeile in a() weiter, nämlich mit dem Aufruf von d() (
). Ebenso wie c() ruft auch d() nichts auf, sondern gibt lediglich d() starts und d() returns aus, bevor sie die Steuerung an die Zeile in a() zurückgibt, in der sie aufgerufen wurde. Die letzte Zeile in a()gibt a() returns aus und springt dann zu dem ursprünglichen Aufruf von a() am Ende des Programms zurück (
).
Mithilfe des Aufrufstacks merkt sich Python, wohin die Steuerung nach einem Funktionsaufruf jeweils zurückspringen muss. Dieser Stack wird jedoch nicht in einer Variablen gespeichert, sondern hinter den Kulissen von Python gehandhabt. Wenn das Programm eine Funktion aufruft, erstellt Python ein Frameobjekt an der Spitze des Aufrufstacks. In Frameobjekten ist die Zeilennummer des ursprünglichen Funktionsaufrufs gespeichert, sodass sich Python merken kann, wohin es zurückspringen muss. Erfolgt ein weiterer Funktionsaufruf, legt Python im Stack ein weiteres Frameobjekt auf das vorhergehende.
Beim Rücksprung aus einer aufgerufenen Funktion entfernt Python das zugehörige Frameobjekt vom Stapel und fährt mit der Ausführung in der Zeile mit der gespeicherten Nummer fort. Frameobjekte werden dabei immer oben auf den Stapel gelegt und auch dort wieder von ihm heruntergenommen, niemals an einer anderen Stelle. Abb. 3–2 zeigt die verschiedenen Zustände des Aufrufstacks von abcdCallStack.py, während die einzelnen Funktionen aufgerufen werden und zurückspringen.
Abb. 3–2 Frameobjekte im Aufrufstack im Verlauf der Funktionsaufrufe und Rücksprünge in abcdCallStack.py
Das oberste Objekt auf dem Stack besagt, welche Funktion gerade ausgeführt wird. Wenn der Aufrufstack leer ist, erfolgt die Ausführung außerhalb von Funktionen.
Beim Aufrufstack handelt es sich um eine technische Hintergrundeinrichtung, die Sie nicht unbedingt kennen müssen, um Programme schreiben zu können. Es reicht zu wissen, dass Funktionsaufrufe zu der Zeile zurückkehren, von der aus sie aufgerufen wurden. Allerdings erleichtern Kenntnisse des Aufrufstacks das Verständnis der lokalen und globalen Gültigkeitsbereiche, die wir uns im folgenden Abschnitt ansehen.
Lokaler und globaler Gültigkeitsbereich
Parameter und Variablen, die innerhalb einer aufgerufenen Funktion zugewiesen werden, befinden sich im lokalen Gültigkeitsbereich der Funktion. Dagegen haben Variablen, die außerhalb von Funktionen zugewiesen werden, einen globalen Gültigkeitsbereich. Variablen in einem lokalen Gültigkeitsbereich werden als lokale Variablen bezeichnet, Variablen im globalen Gültigkeitsbereich als globale Variablen. Eine Variable ist entweder lokal oder global, aber niemals beides.
Den Gültigkeitsbereich können Sie sich wie einen Behälter für Variablen vorstellen. Wird ein Gültigkeitsbereich zerstört, so gehen alle Werte der darin enthaltenen Variablen verloren. Es gibt nur einen globalen Gültigkeitsbereich. Er wird erstellt, wenn das Programm beginnt, und am Ende des Programms zerstört. Damit sind alle seine Variablen vergessen. Wäre das nicht der Fall, so würden beim nächsten Start des Programms alle Variablen immer noch die Werte aufweisen, die sie bei der letzten Ausführung hatten.
Ein lokaler Gültigkeitsbereich entsteht beim Aufruf einer Funktion. Jegliche Variablen, die in dieser Funktion zugewiesen werden, befinden sich in diesem lokalen Gültigkeitsbereich. Wenn die Funktion die Steuerung zurückgibt, wird der lokale Gültigkeitsbereich zerstört, sodass seine Variablen verloren gehen. Beim nächsten Aufruf der Funktion sind die Werte, die beim letzten Aufruf in den lokalen Variablen gespeichert waren, nicht mehr vorhanden. Die lokalen Variablen werden auch in Frameobjekten auf dem Aufrufstack abgelegt.
Das Prinzip der Gültigkeitsbereiche hat einige wichtige Auswirkungen:
Code im globalen Gültigkeitsbereich, also außerhalb von Funktionen, kann keine lokalen Variablen nutzen.
Code im lokalen Gültigkeitsbereich kann dagegen auf globale Variablen zugreifen.
Code im lokalen Gültigkeitsbereich einer Funktion kann keine Variablen aus anderen lokalen Gültigkeitsbereichen nutzen.
Sie können für zwei Variablen den gleichen Namen wählen, sofern sie sich in unterschiedlichen Gültigkeitsbereichen befinden. Es kann also beispielsweise sowohl eine lokale als auch eine globale Variable namens spam geben.
Warum gibt es in Python verschiedene Gültigkeitsbereiche, anstatt alle Variablen global zu machen? Wenn eine Variable durch den Code in einem bestimmten Funktionsaufruf bearbeitet wird, dann interagiert die Funktion mit dem Rest des Programms nur durch ihre Parameter und den Rückgabewert. Bei einem Fehler schränkt das die Menge der Codezeilen ein, die dafür verantwortlich sein können. Wenn in einem Programm, das ausschließlich globale Variablen enthält, ein Fehler dafür sorgt, dass einer Variablen ein falscher Wert zugewiesen wird, ist es ziemlich schwer, die entsprechende Stelle zu finden. Dieser Wert könnte überall in dem Programm zugewiesen worden sein – und das kann irgendwo in Hunderten oder gar Tausenden von Zeilen sein! Wenn aber eine lokale Variable einen falschen Wert hat, dann wissen Sie, dass er nur im Code der entsprechenden Funktion zugewiesen worden sein kann.
In kurzen Programmen ist die Verwendung globaler Variablen kein Problem, doch bei längeren Programmen sollten Sie sich lieber nicht darauf stützen.
Lokale Variablen können im globalen Gültigkeitsbereich nicht verwendet werden
Wenn Sie das folgende Programm ausführen, erhalten Sie eine Fehlermeldung:
def spam():
eggs = 31337 
spam()
print(eggs)
Die Ausgabe lautet wie folgt:
Traceback (most recent call last):
File "C:/test1.py", line 4, in <module>
print(eggs)
NameError: name 'eggs' is not defined
Das liegt daran, dass die Variable eggs nur in dem lokalen Gültigkeitsbereich existiert, der beim Aufruf von spam() erstellt wird (
). Sobald die Programmausführung spam verlässt, wird dieser lokale Gültigkeitsbereich aber zerstört, weshalb es keine Variable namens eggs mehr gibt. Wenn das Programm versucht, print(eggs) auszuführen, meldet Python, dass eggs nicht definiert ist. Wenn Sie ein wenig darüber nachdenken, ist das auch tatsächlich sinnvoll. Während sich die Programmausführung im globalen Gültigkeitsbereich bewegt, gibt es keine lokalen Gültigkeitsbereiche und damit kann es auch keine lokalen Variablen geben. Aus diesem Grunde lassen sich im globalen Gültigkeitsbereich ausschließlich globale Variablen verwenden.
Lokale Gültigkeitsbereiche können keine Variablen aus anderen lokalen Gültigkeitsbereichen verwenden
Beim Aufruf einer Funktion wird ein neuer lokaler Gültigkeitsbereich erstellt. Das gilt auch dann, wenn die Funktion aus einer anderen Funktion heraus aufgerufen wird. Betrachten Sie dazu das folgende Programm:
def spam():
eggs = 99 
bacon() 
print(eggs) 
def bacon():
ham = 101
eggs = 0 
spam() 
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/otherlocalscopes/ ansehen. Zu Beginn wird hier die Funktion spam() aufgerufen (
) und damit ein lokaler Gültigkeitsbereich erstellt. Die lokale Variable eggs (
) wird auf 99 gesetzt. Daraufhin wird die Funktion bacon() aufgerufen (
) und ein zweiter lokaler Gültigkeitsbereich erstellt. Mehrere lokale Gültigkeitsbereiche können zur selben Zeit existieren. In diesem neuen lokalen Gültigkeitsbereich wird die lokale Variable ham auf 101 gesetzt. Außerdem wird eine lokale Variable namens eggs erstellt und auf 0 gesetzt (
). Allerdings ist dies eine andere Variable als die im lokalen Gültigkeitsbereich von spam().
Wenn bacon() die Steuerung zurückgibt, wird der lokale Gültigkeitsbereich für diesen Aufruf und damit auch die Variable eggs zerstört. Die Programmausführung fährt mit der Funktion spam() fort, um den Wert von eggs auszugeben (
). Der lokale Gültigkeitsbereich für den Aufruf von spam() existiert immer noch. Die einzige Variable namens eggs, die es jetzt noch gibt, ist diejenige der Funktion spam(), die auf 99 gesetzt wurde. Das ist der Wert, den das Programm ausgibt.
Was lernen wir daraus? Lokale Variablen in einer Funktion sind komplett von den lokalen Variablen in einer anderen Funktion getrennt.
Globale Variablen können von einem lokalen Gültigkeitsbereich aus gelesen werden
Betrachten Sie das folgende Programm:
def spam():
print(eggs)
eggs = 42
spam()
print(eggs)
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/readglobal/ ansehen. Da es in der Funktion spam() keinen Parameter namens eggs und auch keinen Code gibt, der eggs bei der Verwendung in spam() einen Wert zuweist, geht Python davon aus, dass hier auf die globale Variable eggs verwiesen wird. Daher gibt das vorstehende Programm den Wert 42 aus.
Lokale und globale Variablen mit demselben Namen
In Python ist es zwar technisch möglich, denselben Namen für eine globale und eine lokale Variable und für lokale Variablen in verschiedenen Gültigkeitsbereichen zu verwenden, doch um sich das Leben zu erleichtern, sollten Sie so etwas tunlichst vermeiden. Um zu sehen, was in einem solchen Fall geschieht, geben Sie folgenden Code in den Dateieditor ein und speichern ihn als localGlobalSameName.py:
def spam():
eggs = 'spam local' 
print(eggs) # Gibt 'spam local' aus
def bacon():
eggs = 'bacon local' 
print(eggs) # Gibt 'bacon local' aus
spam()
print(eggs) # Gibt 'bacon local' aus
eggs = 'global' 
bacon()
print(eggs) # Gibt 'global' aus
Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:
bacon local
spam local
bacon local
global
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/localglobalsamename/ ansehen. Tatsächlich enthält dieses Programm drei verschiedene Variablen, die alle den Namen eggs tragen, was nicht gerade übersichtlich ist. Es handelt sich dabei um folgende Variablen:
1 Die Variable eggs im lokalen Gültigkeitsbereich des Aufrufs von spam() ()
2 Die Variable eggs im lokalen Gültigkeitsbereich des Aufrufs von bacon() ()
3 Die Variable eggs im globalen Gültigkeitsbereich ()
Da diese drei Variablen alle denselben Namen haben, kann es ziemlich kompliziert werden, nachzuvollziehen, welche zu einem bestimmten Zeitpunkt gerade verwendet wird. Daher sollten Sie es vermeiden, Variablen in unterschiedlichen Gültigkeitsbereichen denselben Namen zu geben.
Die Anweisung global
Wenn Sie innerhalb einer Funktion eine globale Variable bearbeiten wollen, müssen Sie die Anweisung global verwenden. Eine Zeile wie global eggs am Anfang einer Funktion weist Python an: »In dieser Funktion bezieht sich eggs auf die globale Variable, also erstelle keine lokale Variable mit diesem Namen!« Geben Sie als Beispiel den folgenden Code in den Dateieditor ein und speichern Sie ihn als globalStatement.py:
def spam():
global eggs 
eggs = 'spam' 
eggs = 'global'
spam()
print(eggs)
Wenn Sie dieses Programm ausführen, gibt der letzte Aufruf von print() Folgendes aus:
spam
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/globalstatement/ ansehen. Da eggs am Anfang von spam() als global deklariert ist (
), erfolgt die Zuweisung von 'spam' zur globalen Variablen eggs (
). Es wird keine lokale Variable namens eggs erstellt.
Es gibt vier Regeln, um lokale und globale Variablen zu unterscheiden:
Wenn eine Variable in einem globalen Gültigkeitsbereich verwendet wird (also außerhalb irgendeiner Funktion), dann handelt es sich um eine globale Variable.
Wird in einer Funktion die Anweisung global für die Variable verwendet, handelt es sich um eine globale Variable.
Wird die Variable in einer Zuweisungsanweisung innerhalb der Funktion verwendet, handelt es sich um eine lokale Variable.
Wird die Variable dagegen nicht in einer Zuweisungsanweisung verwendet, handelt es sich um eine globale Variable.
Um ein besseres Gefühl für diese Regeln zu bekommen, schauen Sie sich das folgende Beispielprogramm an. Geben Sie den folgenden Code in den Dateieditor ein und speichern Sie ihn als sameNameLocalGlobal.py:
def spam():
global eggs 
eggs = 'spam' # Globale Variable
def bacon():
eggs = 'bacon' # Lokale Variable 
def ham():
print(eggs) # Globale Variable 
eggs = 42 # Globale Variable
spam()
print(eggs)
In der Funktion spam() ist eggs die globale Variable dieses Namens, da zu Anfang die Anweisung global steht (
). Dagegen ist eggs in bacon() eine lokale Variable, da es in dieser Funktion eine Zuweisungsanweisung für sie gibt (
). In ham() ist eggs jedoch wiederum die globale Variable, da es in dieser Funktion zwar keine global-Anweisung, aber auch keine Zuweisungsanweisung gibt. Wenn Sie same NameLocalGlobal.py ausführen, erhalten Sie folgende Ausgabe:
spam
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/sameNameLocalGlobal/ ansehen. Innerhalb einer Funktion ist eine Variable entweder global oder lokal. Es ist nicht möglich, im Code einer Funktion sowohl eine lokale als auch eine globale Variable mit demselben Namen zu verwenden.
Hinweis
Wenn Sie den Wert einer globalen Variablen in einer Funktion ändern wollen, so müssen Sie für diese Variable die Anweisung global verwenden.
Wenn Sie wie in dem folgenden Programm versuchen, eine lokale Variable in einer Funktion zu verwenden, bevor Sie ihr einen Wert zuweisen, gibt Python eine Fehlermeldung aus. Um sich das anzusehen, geben Sie den folgenden Code in den Dateieditor ein und speichern ihn als sameNameError.py:
def spam():
print(eggs) # FEHLER!
eggs = 'spam local' 
eggs = 'global' 
spam()
Wenn Sie dieses Programm ausführen, erhalten Sie folgende Fehlermeldung:
Traceback (most recent call last):
File "C:/sameNameError.py", line 6, in <module>
spam()
File "C:/sameNameError.py", line 2, in spam
print(eggs) # FEHLER!
UnboundLocalError: local variable 'eggs' referenced before assignment
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/sameNameError/ ansehen. Der Grund für diesen Fehler ist, dass Python die Zuweisungsanweisung für eggs in der Funktion spam() sieht (
) und daher davon ausgeht, dass eggs eine lokale Variable ist. Allerdings wird print(eggs) ausgeführt, bevor eggs irgendein Wert zugewiesen wurde, also zu einem Zeitpunkt, an dem die lokale Variable eggs noch gar nicht existiert. In einem solchen Fall greift Python nicht auf die globale Variable eggs zurück (
).
Funktionen als »Blackbox«
Meistens müssen Sie von einer Funktion nur die Eingaben (Parameter) und die Ausgabewerte kennen. Es ist oft gar nicht nötig, sich mit der Frage zu belasten, was der Code der Funktion im Einzelnen macht. Wenn Sie Funktionen von dieser hohen Warte aus betrachten, behandeln Sie sie wie eine Blackbox.
Das ist ein grundlegendes Prinzip in der modernen Programmierung. Weiter hinten in diesem Buch werden Sie einige Module mit Funktionen kennenlernen, die von anderen Personen geschrieben wurden. Wenn Sie neugierig sind, können Sie sich zwar auch den Quellcode ansehen, doch um diese Funktionen nutzen zu können, müssen Sie deren interne Mechanismen nicht kennen. Da empfohlen wird, Funktionen ohne globale Variablen zu schreiben, müssen Sie sich gewöhnlich auch keine Sorgen darüber machen, dass sich der Code der Funktion auf den Rest Ihres Programms auswirkt.
Ausnahmebehandlung
Bis jetzt hat das Auftreten einer Fehlermeldung oder einer Ausnahme (Exception) in Ihren Python-Programmen bedeutet, dass das gesamte Programm abstürzt. In Produktionsprogrammen wollen Sie das natürlich nicht. Stattdessen sollte das Programm in der Lage sein, Fehler zu erkennen, damit umzugehen und dann weiterzulaufen.
Betrachten Sie beispielsweise das folgende Programm, bei dem eine Division durch null vorkommen kann. Geben Sie den folgenden Code im Dateieditor ein und speichern Sie ihn als zeroDivide.py:
def spam(divideBy):
return 42 / divideBy
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))
Hier definieren wir die Funktion spam mit einem Parameter und geben dann die Werte dieser Funktion für verschiedene Parameter aus, um zu sehen, was geschieht. Wenn Sie den vorstehenden Code ausführen, erhalten Sie folgende Ausgabe:
21.0
3.5
Traceback (most recent call last):
File "C:/zeroDivide.py", line 6, in <module>
print(spam(0))
File "C:/zeroDivide.py", line 2, in spam
return 42 / divideBy
ZeroDivisionError: division by zero
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/zerodivide/ ansehen. Der Fehler ZeroDivisionError tritt auf, wenn Sie versuchen, eine Zahl durch null zu teilen. An der Zeilennummer in der Fehlermeldung können Sie ablesen, dass die return-Anweisung in spam() den Fehler verursacht hat.
Fehler lassen sich mit den Anweisungen try und except handhaben. Stellen Sie Code, der einen Fehler verursachen kann, in eine try-Klausel. Wenn tatsächlich ein Fehler auftritt, rückt die Programmausführung zum Beginn der anschließenden except-Klausel vor.
In unserem Beispiel können Sie also den Code für die Division in eine try-Klausel stellen und in einer except-Klausel Code für den Fall angeben, dass eine Division durch null auftritt:
def spam(divideBy):
try:
return 42 / divideBy
except ZeroDivisionError:
print('Error: Invalid argument.')
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))
Wenn Code in einer try-Klausel einen Fehler hervorruft, fährt die Programmausführung unmittelbar mit dem Code in der except-Klausel fort und läuft anschließend ganz normal weiter. Der vorstehende Code führt zu folgender Ausgabe:
21.0
3.5
Error: Invalid argument.
None
42.0
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/tryexceptzerodivide/ ansehen. Wenn in einem try-Block Funktionsaufrufe stehen, werden auch jegliche Fehler abgefangen, die dabei auftreten. Betrachten Sie das folgende Programm, bei dem die Aufrufe von spam() in einem try-Block stehen:
def spam(divideBy):
return 42 / divideBy
try:
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))
except ZeroDivisionError:
print('Error: Invalid argument.')
Dieses Programm führt zu folgender Ausgabe:
21.0
3.5
Error: Invalid argument.
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/spamintry/ ansehen. In diesem Fall wird print(spam(1)) niemals ausgeführt, da die Ausführung nach dem Code in der except-Klausel nicht wieder in den try-Block zurückspringt, sondern ganz normal im Programm fortgesetzt wird.
Ein kurzes Programm: Zickzackmuster
Mit den bisher erlernten Programmiertechniken wollen wir nun ein kleines Animationsprogramm schreiben. Es erstellt ein Zickzackmuster, bis der Benutzer es anhält, indem er auf die Stop-Schaltfläche von Mu klickt oder
+
drückt. Bei der Ausführung dieses Programms sehen Sie die folgende Ausgabe:
********
********
********
********
********
********
********
********
********
Geben Sie den folgenden Quellcode in den Dateieditor ein und speichern Sie die Datei als zigzag.py:
import time, sys
indent = 0 # Anzahl der Zeichen in der Einrückung
indentIncreasing = True # Gibt an, ob die Einrückung größer wird oder nicht
try:
while True: # Hauptprogrammschleife
print(' ' * indent, end='')
print('********')
time.sleep(0.1) # Hält 1/10 Sekunde lang an
if indentIncreasing:
# Erhöht die Anzahl der Leerzeichen
indent = indent + 1
if indent == 20:
# Ändert die Richtung
indentIncreasing = False
else:
# Verringert die Anzahl der Leerzeichen
indent = indent - 1
if indent == 0:
# Ändert die Richtung
indentIncreasing = True
except KeyboardInterrupt:
sys.exit()
Sehen wir uns diesen Code nun Zeile für Zeile an.
import time, sys
indent = 0 # Anzahl der Zeichen in der Einrückung
indentIncreasing = True # Gibt an, ob die Einrückung größer wird oder nicht
Als Erstes importieren wir die Module time und sys. Des Weiteren verwendet das Programm zwei Variablen: Mit indent merkt es sich, wie viele Leerzeichen vor der Reihe von acht Sternchen stehen, während der boolesche Wert in indentIncreasing bestimmt, ob die Anzahl der Leerzeichen größer oder kleiner werden soll.
try:
while True: # Hauptprogrammschleife
print(' ' * indent, end='')
print('********')
time.sleep(0.1) # Hält 1/10 Sekunde lang an
Der Rest des Programms steht in einer try-Anweisung. Wenn der Benutzer während der Ausführung eines Python-Programms
+
drückt, wird die Ausnahme KeyboardInterrupt ausgelöst. Ohne eine try-except-Anweisung, die diese Ausnahme abfängt, würde das Programm mit einer unschönen Fehlermeldung abstürzen. In unserem Programm soll die Ausnahme KeyboardInterrupt aber sauber durch den Aufruf von sys.exit() abgefangen werden. (Der Code dafür befindet sich in der except-Anweisung am Ende des Programms.)
Die Endlosschleife while True: wiederholt unaufhörlich die Anweisungen in unserem Programm. Darin wird mit ' ' * indent jeweils die richtige Menge an Leerzeichen zur Einrückung ausgegeben. Da hinter diesen Leerzeichen nicht automatisch ein Zeilenumbruch erfolgen soll, übergeben wir außerdem end='' an den ersten Aufruf von print(). Ein zweiter print()-Aufruf gibt die Sternchenreihe aus. Die Funktion time.sleep() haben wir noch nicht behandelt; es reicht hier zu wissen, dass sie an dieser Stelle des Programms eine Pause von einer Zehntelsekunde Dauer einschiebt.
if indentIncreasing:
# Erhöht die Anzahl der Leerzeichen
indent = indent + 1
if indent == 20:
# Ändert die Richtung
indentIncreasing = False
Als Nächstes ändern wir die Länge der Einrückung für die nächste Sternchenreihe. Wenn indentIncreasing den Wert True hat, erhöhen wir indent um 1. Erreicht die Einrückung aber eine Länge von 20, soll sie wieder abnehmen.
else:
# Verringert die Anzahl der Leerzeichen
indent = indent - 1
if indent == 0:
# Ändert die Richtung
indentIncreasing = True
Hat indentIncreasing den Wert False, müssen wir indent jeweils um 1 verringern. Sobald indent dadurch auf 0 sinkt, soll die Einrückung wieder wachsen. In jedem Fall springt die Programmausführung zurück zum Anfang der Hauptschleife, um wieder mit der Ausgabe der Sternchenreihen zu beginnen.
except KeyboardInterrupt:
sys.exit()
Wenn der Benutzer zu irgendeinem Zeitpunkt, an dem sich die Programmausführung in dem try-Block befindet,
+
drückt, wird die Ausnahme KeyboardInterrupt ausgelöst und von der except-Anweisung behandelt. Die Programmausführung geht dann in dem except-Block weiter, der sys.exit() ausführt und damit das Programm beendet. Dadurch hat der Benutzer die Möglichkeit, das Programm zu beenden, auch wenn es sich bei der Hauptschleife um eine Endlosschleife handelt.
Zusammenfassung
Funktionen sind die wichtigste Möglichkeit, um Ihren Code logisch zu gruppieren. Da Variablen in Funktion eigene lokale Gültigkeitsbereiche haben, kann der Code in einer Funktion die Werte von Variablen in anderen Funktionen nicht unmittelbar beeinflussen. Das schränkt den Umfang des Codes ein, der die Werte von Variablen ändern kann, was sich wiederum beim Debugging als sehr hilfreich erweist.
Funktionen bilden eine hervorragende Möglichkeit, um Ihren Code zu gliedern. Betrachten Sie sie als Blackbox: Sie haben Eingaben in der Form von Parametern und Ausgaben in Form von Rückgabewerten, aber ihr interner Code wirkt sich nicht auf die Variablen in anderen Funktionen aus.
In vorherigen Kapiteln führte ein einziger Fehler zum Absturz des gesamten Programms. In diesem Kapitel haben Sie die Anweisungen try und except kennengelernt, mit denen besonderer Code ausgeführt werden kann, wenn ein Fehler erkannt wurde. Das macht Ihren Code widerstandsfähiger gegen gängige Fehler.
Wiederholungsfragen
1 Warum ist es von Vorteil, in Ihren Programmen Funktionen zu verwenden?
2 Wann wird der Code in einer Funktion ausgeführt – bei der Definition oder beim Aufruf der Funktion?
3 Mit welcher Anweisung erstellen Sie eine Funktion?
4 Was ist der Unterschied zwischen einer Funktion und einem Funktionsaufruf?
5 Wie viele globale Gültigkeitsbereiche hat ein Python-Programm? Wie viele lokale Gültigkeitsbereiche?
6 Was geschieht mit den Variablen in einem lokalen Gültigkeitsbereich, wenn der Funktionsaufruf die Steuerung zurückgibt?
7 Was ist ein Rückgabewert? Kann ein Rückgabewert Teil eines Ausdrucks sein?
8 Welchen Rückgabewert hat der Aufruf einer Funktion, die keine return-Anweisung hat?
9 Wie können Sie dafür sorgen, dass in einer Funktion eine globale Variable verwendet wird?
10 Welchen Datentyp hat None?
11 Was macht die Anweisung import areallyourpetsnamederic?
12 Wie rufen Sie die Funktion bacon() aus dem Modul spam auf, nachdem Sie spam importiert haben?
13 Wie verhindern Sie, dass ein Programm beim Auftreten eines Fehlers abstürzt?
14 Was steht in einer try-Klausel und was in der except-Klausel?
Übungsprojekte
Schreiben Sie zur Übung Programme, die die folgenden Aufgaben lösen.
Die Collatz-Folge
Schreiben Sie die Funktion collatz() mit dem Parameter number. Wenn number eine gerade Zahl ist, soll diese Funktion number // 2 ausgeben und zurückgeben. Ist number dagegen ungerade, soll collatz das Ergebnis von 3 * number + 1 ausgeben und zurückgeben.
Schreiben Sie anschließend ein Programm, das eine ganze Zahl als Benutzereingabe entgegennimmt und anschließend für diese Zahl und danach für die Rückgabewerte immer wieder collatz() aufruft, bis die Funktion den Wert 1 zurückgibt. (Erstaunlicherweise funktioniert das mit jeder beliebigen ganzen Zahl als Ausgangspunkt. Früher oder später erreichen Sie mit dieser Folge den Wert 1. Selbst Mathematiker wissen nicht, warum das so ist. Dieses Programm veranschaulicht die sogenannte Collatz-Folge, die manchmal auch als »das einfachste unlösbare Problem der Mathematik« bezeichnet wird.)
Vergessen Sie nicht, den Rückgabewert von input() mithilfe von int() in einen Integer umzuwandeln, denn zunächst einmal ist er ein String.
Tipp: Der Integer number ist gerade, wenn number % 2 == 0, und ungerade, wenn number % 2 == 1.
Die Ausgabe dieses Programms sieht wie folgt aus:
Enter number:
3
10
5
16
8
4
2
1
Eingabevalidierung
Ergänzen Sie das vorherige Projekt um try- und except-Anweisungen, um festzustellen, ob der Benutzer etwas anderes als einen Integer eingegeben hat. Normalerweise gibt die Funktion int() den Fehler ValueError zurück, wenn ihr etwas anderes als ein String übergeben wird, der die Ziffernfolge einer ganzen Zahl darstellt, z. B. int('puppy'). Geben Sie in der except-Klausel eine Meldung aus, die den Benutzer darauf hinweist, dass er eine ganze Zahl eingeben muss.
4
Listen
Bevor Sie ernsthaft damit beginnen können, Programme zu schreiben, müssen Sie sich noch mit einem weiteren Thema beschäftigen, nämlich mit den miteinander verwandten Datentypen für Listen und Tupel. Beide können mehrere Werte enthalten, was es einfacher macht, Programme zu schreiben, die mit großen Datenmengen umgehen. Da Listen wiederum andere Listen einschließen können, ist es damit auch möglich, Daten hierarchisch zu gliedern.
In diesem Kapitel beschäftigen wir uns mit den Grundlagen von Listen. Außerdem stelle ich Methoden vor, also Funktionen, die an die Werte eines bestimmten Datentyps gebunden sind. Des Weiteren werde ich kurz auch auf sequenzielle Datentypen im Allgemeinen (Listen, Tupel und Strings) und ihre Unterschiede und Gemeinsamkeiten eingehen. Im nächsten Kapitel geht es dann um den Datentyp für Dictionaries (Wörterbücher).
Der Datentyp für Listen
Eine Liste ist ein Wert, der mehrere Werte in einer geordneten Reihenfolge enthält, etwa wie folgt: ['cat', bat', 'rat', 'elephant']. So wie Anfang und Ende von Stringwerten durch Anführungszeichen gekennzeichnet sind, so beginnen und enden Listen mit eckigen Klammern. Der Begriff Listenwert bezeichnet dabei die Liste selbst (da sie wie jeder andere Wert in einer Variablen gespeichert oder einer Funktion übergeben werden kann), nicht die darin enthaltenen Werte. Diese werden Elemente genannt und sind durch Kommata getrennt. Zur Veranschaulichung geben Sie Folgendes in die interaktive Shell ein:
>>> [1, 2, 3]
[1, 2, 3]
>>> ['cat', 'bat', 'rat', 'elephant']
['cat', 'bat', 'rat', 'elephant']
>>> ['hello', 3.1415, True, None, 42]
['hello', 3.1415, True, None, 42]
>>> spam = ['cat', 'bat', 'rat', 'elephant'] 
>>> spam
['cat', 'bat', 'rat', 'elephant']
Der Variablen spam wird nur ein einziger Wert zugewiesen, nämlich der Listenwert (
), der aber selbst wiederum mehrere andere Werte enthält.
Übrigens gibt es vergleichbar mit dem leeren String '' auch eine leere Liste [], die keinerlei Elemente enthält.
Einzelne Elemente einer Liste mithilfe des Index abrufen
Wenn Sie die Liste ['cat', bat', 'rat', 'elephant'] in der Variablen spam gespeichert haben, wird spam[0] zu 'cat' ausgewertet, spam[1] zu 'bat' usw. Der Integer in den eckigen Klammern, der die Position in der Liste angibt, wird als Index bezeichnet. Das erste Element in einer Liste hat den Index 0, das zweite den Index 1, das dritte den Index 2 usw. Abb. 4–1 zeigt den Listenwert, der spam zugeordnet ist, und gibt an, wozu die einzelnen Indexausdrücke ausgewertet werden. Da der erste Index 0 ist, ist der letzte eins weniger als die Länge der Liste. Bei einer Liste mit vier Einträgen lautet der letzte Index also 3.
Abb. 4–1Ein Listenwert gespeichert in der Variablen spam. Die Pfeile zeigen, auf welche Elemente sich die Indizes jeweils beziehen.
Geben Sie zur Veranschaulichung die folgenden Ausdrücke in die interaktive Shell ein. Zu Anfang weisen Sie der Variablen spam dabei eine Liste zu.
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[0]
'cat'
>>> spam[1]
'bat'
>>> spam[2]
'rat'
>>> spam[3]
'elephant'
>>> ['cat', 'bat', 'rat', 'elephant'][3]
'elephant'
>>> 'Hello ' + spam[0] 
'Hello cat' 
>>> 'The ' + spam[1] + ' ate the ' + spam[0] + '.'
'The bat ate the cat.'
Der Ausdruck 'Hello ' + spam[0] (
) wird dabei zu 'Hello ' + 'cat' ausgewertet, da spam[0] den String 'cat' bezeichnet. Dieser Ausdruck wiederum ergibt den Stringwert 'Hello cat' (
).
Wenn Sie einen Index verwenden, der über die Anzahl der Elemente in der Liste hinausgeht, gibt Python die Fehlermeldung IndexError aus:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[10000]
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
spam[10000]
IndexError: list index out of range
Indizes können immer nur Integerwerte sein, keine Fließkomazahlen. Das folgende Beispiel ruft den Fehler TypeError hervor:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[1]
'bat'
>>> spam[1.0]
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
spam[1.0]
TypeError: list indices must be integers or slices, not float
>>> spam[int(1.0)]
'bat'
Listen können auch andere Listen enthalten. Um auf die Elemente dieser Listen zuzugreifen, verwenden Sie Mehrfachindizes:
>>> spam = [['cat', 'bat'], [10, 20, 30, 40, 50]]
>>> spam[0]
['cat', 'bat']
>>> spam[0][1]
'bat'
>>> spam[1][4]
50
Der erste Index gibt an, welche Liste abgerufen werden soll, während der zweite das Element in dieser Liste bezeichnet. Beispielsweise führt spam[0][1] zu 'bat', dem zweiten Element in der ersten Liste. Wenn Sie nur einen Index angeben, gibt das Programm die komplette Liste an dieser Indexposition aus.
Negative Indizes
Die Indexzahlen verlaufen von null aufwärts, doch Sie können auch negative Integer als Index angeben. Dabei bezeichnet der Wert -1 den letzten Index in der Liste, -2 den vorletzten usw. Geben Sie folgenden Code in die interaktive Shell ein:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[-1]
'elephant'
>>> spam[-3]
'bat'
>>> 'The ' + spam[-1] + ' is afraid of the ' + spam[-3] + '.'
'The elephant is afraid of the bat.'
Teillisten mithilfe von Slices abrufen
Mit einem Index können Sie ein einzelnes Element aus einer Liste abrufen, mit einem Slice dagegen mehrere Elemente in Form einer neuen Liste. Wie ein Index wird ein Slice in eckigen Klammern angegeben, allerdings in Form von zwei durch einen Doppelpunkten getrennten Integern. Beachten Sie den Unterschied:
spam[2] bezeichnet einen Index in einer Liste (ein Integer)
spam[1:4] bezeichnet einen Slice in einer Liste (zwei Integer)
Der erste Integer in einem Slice gibt die Indexposition an, bei der der Slice beginnt, der zweite Integer diejenige, bei der er endet. Der Slice enthält alle Werte vom ersten bis zum zweiten angegebenen Index, allerdings ohne den Wert dieses zweiten Index. Das Ergebnis eines Slice ist ein neuer Listenwert. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[0:4]
['cat', 'bat', 'rat', 'elephant']
>>> spam[1:3]
['bat', 'rat']
>>> spam[0:-1]
['cat', 'bat', 'rat']
Als Kurzschreibweise können Sie auch auf einen oder beide Indizes vor und nach dem Doppelpunkt verzichten. Wenn Sie den ersten Index weglassen, wird 0 angenommen, also der Beginn der Liste, wenn Sie den zweiten Index weglassen, das Ende der Liste. Geben Sie Folgendes in die interaktive Shell ein:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[:2]
['cat', 'bat']
>>> spam[1:]
['bat', 'rat', 'elephant']
>>> spam[:]
['cat', 'bat', 'rat', 'elephant']
Die Länge einer Liste mit len() abrufen
Die Funktion len() kann nicht nur die Anzahl der Zeichen in einem Stringwert nennen, sondern auch die Anzahl der Elemente in dem ihr übergebenen Listenwert. Geben Sie Folgendes in die interaktive Shell ein:
>>> spam = ['cat', 'dog', 'moose']
>>> len(spam)
3
Werte in einer Liste mithilfe des Index ändern
Auf der linken Seite einer Zuweisungsanweisung steht normalerweise ein Variablenname, etwa wie in spam = 42. Sie können jedoch auch einen Listenindex angeben, um den Wert an diesem Index zu ändern. Beispielsweise bedeutet spam[1] = 'aardvark': »Weise dem Element an Index 1 der Liste spam den String 'aardvark' zu.« Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[1] = 'aardvark'
>>> spam
['cat', 'aardvark', 'rat', 'elephant']
>>> spam[2] = spam[1]
>>> spam
['cat', 'aardvark', 'aardvark', 'elephant']
>>> spam[-1] = 12345
>>> spam
['cat', 'aardvark', 'aardvark', 12345]
Listenverkettung und -wiederholung
Listen lassen sich ebenso wie Strings verketten und wiederholen. Der Operator + kombiniert zwei Listen zu einem neuen Listenwert. Auch der Operator * kann mit einer Liste und einem Integerwert verwendet werden, um die Liste zu wiederholen. Geben Sie Folgendes in die interaktive Shell ein:
>>> [1, 2, 3] + ['A', 'B', 'C']
[1, 2, 3, 'A', 'B', 'C']
>>> ['X', 'Y', 'Z'] * 3
['X', 'Y', 'Z', 'X', 'Y', 'Z', 'X', 'Y', 'Z']
>>> spam = [1, 2, 3]
>>> spam = spam + ['A', 'B', 'C']
>>> spam
[1, 2, 3, 'A', 'B', 'C']
Elemente mit del aus einer Liste entfernen
Die Anweisung del löscht die Werte am angegebenen Listenindex, woraufhin alle nachfolgenden Werte einen Indexplatz aufrücken. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> del spam[2]
>>> spam
['cat', 'bat', 'elephant']
>>> del spam[2]
>>> spam
['cat', 'bat']
Die Anweisung del können Sie auch verwenden, um eine einfache Variable zu löschen, also praktisch als Anweisung zum »Aufheben der Zuweisung«. Wenn Sie versuchen, auf die Variable zuzugreifen, nachdem Sie sie gelöscht haben, erhalten Sie die Fehlermeldung NameError, da die Variable nicht mehr existiert.
In der Praxis müssen Sie einfache Variablen jedoch so gut wie nie löschen. Die Anweisung del wird meistens verwendet, um Elemente aus Listen zu entfernen.
Listen verwenden
Einsteiger in die Programmierung sind oft versucht, viele einzelne Variablen zu erstellen, um Gruppen ähnlicher Werte zu speichern. Nehmen wir an, ich möchte die Namen meiner Katzen festhalten. Dann könnte ich Code wie den folgenden schreiben:
catName1 = 'Zophie'
catName2 = 'Pooka'
catName3 = 'Simon'
catName4 = 'Lady Macbeth'
catName5 = 'Fat-tail'
catName6 = 'Miss Cleo'
Das ist jedoch eine schlechte Vorgehensweise. (Außerdem möchte ich betonen, dass ich in Wirklichkeit nicht so viele Katzen habe!) Erstens kann das Programm niemals mehr Katzennamen speichern, als Variablen zur Verfügung stehen, was zu einem Problem führt, wenn sich die Anzahl der Katzen ändert. Bei Programmen, die auf diese Weise geschrieben sind, gibt es gewöhnlich auch eine Menge an doppeltem oder fast identischem Code. Schauen Sie sich einmal an, wie viel doppelten Code es in dem folgenden Programm namens allMyCats1.py gibt:
print('Enter the name of cat 1:')
catName1 = input()
print('Enter the name of cat 2:')
catName2 = input()
print('Enter the name of cat 3:')
catName3 = input()
print('Enter the name of cat 4:')
catName4 = input()
print('Enter the name of cat 5:')
catName5 = input()
print('Enter the name of cat 6:')
catName6 = input()
print('The cat names are:')
print(catName1 + ' ' + catName2 + ' ' + catName3 + ' ' + catName4 + ' ' +
catName5 + ' ' + catName6)
Anstatt mehrerer ähnlicher Variablen können Sie auch eine einzige Variable mit einem Listenwert verwenden. Die folgende verbesserte Version von allMyCats1.py nutzt eine einzige Liste und kann darin so viele Katzennamen speichern, wie der Benutzer eingibt. Geben Sie den folgenden Quellcode in ein Dateieditorfenster ein und speichern Sie ihn als allMyCats2.py:
catNames = []
while True:
print('Enter the name of cat ' + str(len(catNames) + 1) +
' (Or enter nothing to stop.):')
name = input()
if name == '':
break
catNames = catNames + [name] # list concatenation
print('The cat names are:')
for name in catNames:
print(' ' + name)
Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:
Enter the name of cat 1 (Or enter nothing to stop.):
Zophie
Enter the name of cat 2 (Or enter nothing to stop.):
Pooka
Enter the name of cat 3 (Or enter nothing to stop.):
Simon
Enter the name of cat 4 (Or enter nothing to stop.):
Lady Macbeth
Enter the name of cat 5 (Or enter nothing to stop.):
Fat-tail
Enter the name of cat 6 (Or enter nothing to stop.):
Miss Cleo
Enter the name of cat 7 (Or enter nothing to stop.):
The cat names are:
Zophie
Pooka
Simon
Lady Macbeth
Fat-tail
Miss Cleo
Die Ausführung dieser Programme können Sie sich auf https://autbor.com/allmycats1/ und https://autbor.com/allmycats2/ ansehen. Der Vorteil der Verwendung einer Liste besteht darin, dass sich die Daten jetzt in einer Struktur befinden, sodass das Programm sie auf viel flexiblere Art und Weise verarbeiten kann, als wenn sie in mehreren ähnlichen Variablen steckten.
Listen in for-Schleifen
In Kapitel 2 haben Sie gelernt, wie Sie einen Codeblock mithilfe einer for-Schleife eine gegebene Anzahl von Malen ausführen. Technisch gesehen wiederholt eine for-Schleife den Codeblock einmal für jedes Element in einem Listenwert. Nehmen wir an, Sie führen folgenden Code aus:
for i in range(4):
print(i)
Die Ausgabe dieses Programms lautet wie folgt:
0
1
2
3
Der Grund dafür ist, dass der Rückgabewert von range(4) ein sequenzieller Wert ist, den Python ähnlich wie [0, 1, 2, 3] auffasst. (Sequenzen werden weiter hinten in diesem Kapitel im Abschnitt »Sequenzielle Datentypen« behandelt.) Das folgende Programm führt zu der gleichen Ausgabe wie oben:
for i in [0, 1, 2, 3]:
print(i)
Diese for-Schleife durchläuft seine Klausel, wobei die Variable i bei jeder Iteration auf den nächstfolgenden Wert der Liste [0, 1, 2, 3] gesetzt wird.
Eine übliche Python-Technik besteht darin, range(len(eineListe)) in einer for-Schleife zu verwenden, um über die Indizes einer Liste zu iterieren. Geben Sie beispielsweise folgenden Code in die interaktive Shell ein:
>>> supplies = ['pens', 'staplers', 'flamethrowers', 'binders']
>>> for i in range(len(supplies)):
... print('Index ' + str(i) + ' in supplies is: ' + supplies[i])
Index 0 in supplies is: pens
Index 1 in supplies is: staplers
Index 2 in supplies is: flamethrowers
Index 3 in supplies is: binders
Die Verwendung von range(len(supplies)) in dieser for-Schleife ist sehr praktisch, da der Code in der Schleife auf den Index (die Variable i) zugreifen und den Wert an diesem Index (also supplies[i]) lesen kann. Das Beste aber ist, dass range(len(-supplies)) stets sämtliche Indizes von supplies durchläuft, wie viele Elemente die Liste auch immer enthält.
Die Operatoren in und not in
Mit den Operatoren in und not in können Sie feststellen, ob sich ein Wert in einer Liste befindet oder nicht. Wie die anderen Operatoren werden auch in und not in in Ausdrücken verwendet und verknüpfen zwei Werte: den Wert, nach dem in der Liste gesucht werden soll, und die betreffende Liste. Ausgewertet werden diese Ausdrücke zu einem booleschen Wert. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> 'howdy' in ['hello', 'hi', 'howdy', 'heyas']
True
>>> spam = ['hello', 'hi', 'howdy', 'heyas']
>>> 'cat' in spam
False
>>> 'howdy' not in spam
False
>>> 'cat' not in spam
True
In dem folgenden Beispielprogramm gibt der Benutzer den Namen eines Haustiers ein, woraufhin geprüft wird, ob dieser Name in einer Liste von Haustieren steht. Geben Sie den folgenden Code im Dateieditor ein und speichern Sie ihn als myPets.py:
myPets = ['Zophie', 'Pooka', 'Fat-tail']
print('Enter a pet name:')
name = input()
if name not in myPets:
print('I do not have a pet named ' + name)
else:
print(name + ' is my pet.')
Die Ausgabe sieht wie folgt aus:
Enter a pet name:
Footfoot
I do not have a pet named Footfoot
Der Trick mit der Mehrfachzuweisung
Die Mehrfachzuweisung (technisch Tupel Unpacking genannt) ist eine Abkürzung, mit der Sie in einer einzigen Codezeile mehreren Variablen Werte aus einer Liste zuweisen können. Wenn Sie das einzeln tun wollten, müssten Sie wie folgt vorgehen:
>>> cat = ['fat', gray, 'loud']
>>> size = cat[0]
>>> color = cat[1]
>>> disposition = cat[2]
Stattdessen aber können Sie einfach Folgendes schreiben:
>>> cat = ['fat', gray, 'loud']
>>> size, color, disposition = cat
Dabei muss es jedoch genauso viele Variablen wie Elemente in der Liste geben. Anderenfalls meldet Python den Fehler ValueError:
>>> cat = ['fat', gray, 'loud']
>>> size, color, disposition, name = cat
Traceback (most recent call last):
File "<pyshell#84>", line 1, in <module>
size, color, disposition, name = cat
ValueError: not enough values to unpack (expected 4, got 3)
Die Funktion enumerate() für Listen
Um die Indizes der Listeneinträge abzurufen, können Sie in der for-Schleife statt range(len(eineListe)) auch die Funktion enumerate() verwenden. Bei jedem Schleifendurchlauf gibt enumerate() zwei Werte zurück, nämlich den Index eines Eintrags in der Liste und den Eintrag selbst. Der folgende Code führt zum selben Ergebnis wie der aus »Listen in for-Schleifen« weiter vorn in diesem Kapitel:
>>> supplies = ['pens', 'staplers', 'flamethrowers', 'binders']
>>> for index, item in enumerate(supplies):
... print('Index ' + str(index) + ' in supplies is: ' + item)
Index 0 in supplies is: pens
Index 1 in supplies is: staplers
Index 2 in supplies is: flamethrowers
Index 3 in supplies is: binders
Die Funktion enumerate() ist praktisch, wenn Sie im Schleifenblock sowohl den Eintrag als auch seinen Index benötigen.
Die Funktionen random.choice() und random.shuffle() für Listen
Das Modul random enthält zwei Funktionen, die Listen als Argumente entgegennehmen. Die Funktion random.choice() gibt einen zufällig ausgewählten Listeneintrag zurück. Um das auszuprobieren, geben Sie Folgendes in die interaktive Shell ein:
>>> import random
>>> pets = ['Dog', 'Cat', 'Moose']
>>> random.choice(pets)
'Dog'
>>> random.choice(pets)
'Cat'
>>> random.choice(pets)
'Cat'
Sie können sich random.choice(eineListe) als Kurzfassung von eineListe[random.randint(0, len(eineListe) - 1] vorstellen.
Die Funktion random.shuffle() sortiert die Listeneinträge um. Dabei ändert sie unmittelbar die vorhandene Liste, anstatt eine neue zurückzugeben. Das können Sie wie folgt in der interaktiven Shell ausprobieren:
>>> import random
>>> people = ['Alice', 'Bob', 'Carol', 'David']
>>> random.shuffle(people)
>>> people
['Carol', 'David', 'Alice', 'Bob']
>>> random.shuffle(people)
>>> people
['Alice', 'David', 'Bob', 'Carol']
Erweiterte Zuweisungsoperatoren
Nachdem Sie einer Variablen einen Wert zugewiesen haben, werden Sie sie im weiteren Verlauf gewöhnlich noch oft verwenden. Im folgenden Beispiel wird der Variablen spam zunächst der Wert 42 zugewiesen, der dann später um 1 erhöht wird:
>>> spam = 42
>>> spam = spam + 1
>>> spam
43
Als Kurzschreibweise können Sie dazu auch den erweiterten Zuweisungsoperator += verwenden:
>>> spam = 42
>>> spam += 1
>>> spam
43
Wie Sie in Tabelle 4–1 sehen, gibt es erweiterte Zuweisungsoperatoren für +, -, *, / und %:
| Erweiterter Zuweisungsoperator | Entsprechende Zuweisungsanweisung |
| spam += 1 | spam = spam + 1 |
| spam -= 1 | spam = spam - 1 |
| spam *= 1 | spam = spam * 1 |
| spam /= 1 | spam = spam / 1 |
| spam %= 1 | spam = spam % 1 |
Tab. 4–1 Erweiterte Zuweisungsoperatoren
Der Operator += kann auch für die String- und Listenverkettung eingesetzt werden, der Operator *= auch für String- und Listenwiederholung. Geben Sie Folgendes in die interaktive Shell ein:
>>> spam = 'Hello'
>>> spam += ' world!'
>>> spam
'Hello world!'
>>> bacon = ['Zophie']
>>> bacon *= 3
>>> bacon
['Zophie', 'Zophie', 'Zophie']
Methoden
Eine Methode ist eine Funktion, die für einen Wert aufgerufen wird. Wenn wir beispielsweise einen Listenwert in spam gespeichert haben, können wir die Listenmethode index() (die ich im Folgenden erklären werde) für diese Liste wie folgt aufrufen: spam.index('hello').
Jeder Datentyp verfügt über seinen eigenen Satz von Methoden. So hat der Listendatentyp beispielsweise einige sehr nützliche Methoden, um Elemente in einer Liste zu finden, hinzuzufügen, zu entfernen oder auf andere Weise zu bearbeiten.
Elemente in einer Liste mit der Methode index() finden
Für Listen gibt es die Methode index(). Wenn der ihr übergebene Wert in der Liste vorhanden ist, so gibt sie seinen Index zurück. Existiert der betreffende Wert in der Liste nicht, gibt Python den Fehler ValueError aus. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> spam = ['hello', 'hi', 'howdy', 'heyas']
>>> spam.index('hello')
0
>>> spam.index('heyas')
3
>>> spam.index('howdy howdy howdy')
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
spam.index('howdy howdy howdy')
ValueError: 'howdy howdy howdy' is not in list
Kommt ein Wert mehrfach in einer Liste vor, so wird der Index des ersten Auftretens zurückgegeben. Im folgenden Beispiel gibt index() nicht 3 zurück, sondern 1:
>>> spam = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka']
>>> spam.index('Pooka')
1
Elemente mit den Methoden append() und insert() zu Listen hinzufügen
Um zu einer Liste neue Elemente hinzuzufügen, verwenden Sie die Methoden append() und insert(). Im folgenden Beispiel wird die Methode append() auf die Liste in der Variablen spam angewendet:
>>> spam = ['cat', 'dog', 'bat']
>>> spam.append('moose')
>>> spam
['cat', 'dog', 'bat', 'moose']
Wie Sie sehen, hängt die Methode append() ihr Argument an das Ende der Liste an. Mit insert() dagegen können Sie einen Wert an einer beliebigen Indexposition in der Liste einfügen. Das erste Argument von insert() ist der Index für den neuen Wert, das zweite der einzufügende Wert. Geben Sie Folgendes in die interaktive Shell ein:
>>> spam = ['cat', 'dog', 'bat']
>>> spam.insert(1, 'chicken')
>>> spam
['cat', 'chicken', 'dog', 'bat']
Beachten Sie, dass die korrekte Schreibweise spam.append('moose') bzw. spam.insert(1, 'chicken') lautet und nicht etwa spam = spam.append('moose') oder spam = spam.insert(1, 'chicken'). Weder append() noch insert() geben den neuen Wert von spam zurück. (Der Rückgabewert beider Methoden ist in Wirklichkeit None, und das ist nicht das, was Sie hier als neuen Variablenwert speichern wollen!) Stattdessen wird die Liste unmittelbar geändert. Mehr darüber erfahren Sie im Abschnitt »Veränderbare und unveränderbare Datentypen« weiter hinten in diesem Kapitel.
Methoden gehören jeweils zu einem einzigen Datentyp. So sind append() und insert() Listenmethoden, die nur für Listenwerte aufgerufen werden können, nicht aber für andere Werte wie etwa Strings oder Integer. Wenn Sie Folgendes in die interaktive Shell eingeben, erhalten Sie die Fehlermeldung AttributeError:
>>> eggs = 'hello'
>>> eggs.append('world')
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
eggs.append('world')
AttributeError: 'str' object has no attribute 'append'
>>> bacon = 42
>>> bacon.insert(1, 'world')
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
bacon.insert(1, 'world')
AttributeError: 'int' object has no attribute 'insert'
Elemente mit remove() aus Listen entfernen
Wenn Sie die Methode remove() für eine Liste aufrufen, übergeben Sie ihr den Wert, den Sie aus dieser Liste entfernen möchten. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam.remove('bat')
>>> spam
['cat', 'rat', 'elephant']
Wenn Sie versuchen, einen Wert aus einer Liste zu entfernen, der gar nicht vorhanden ist, erhalten Sie den Fehler ValueError:
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam.remove('chicken')
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
spam.remove('chicken')
ValueError: list.remove(x): x not in list
Kommt der Wert mehrmals in der Liste vor, so wird nur das erste Vorkommen entfernt:
>>> spam = ['cat', 'bat', 'rat', 'cat', 'hat', 'cat']
>>> spam.remove('cat')
>>> spam
['bat', 'rat', 'cat', 'hat', 'cat']
Wenn Sie den Index des Elements kennen, das Sie aus einer Liste löschen wollen, können Sie die Anweisung del verwenden; kennen Sie dagegen den Wert des Elements, so nutzen Sie zum Entfernen remove().
Elemente in einer Liste mit sort() sortieren
Listen von Zahlenwerten oder Strings lassen sich mit der Methode sort() sortieren. Betrachten Sie dazu das folgende Beispiel:
>>> spam = [2, 5, 3.14, 1, -7]
>>> spam.sort()
>>> spam
[-7, 1, 2, 3.14, 5]
>>> spam = ['ants', 'cats', 'dogs', 'badgers', 'elephants']
>>> spam.sort()
>>> spam
['ants', 'badgers', 'cats', 'dogs', 'elephants']
Wenn Sie True für das Schlüsselwortargument reverse übergeben, sortiert sort() die Elemente in umgekehrter Reihenfolge:
>>> spam.sort(reverse=True)
>>> spam
['elephants', 'dogs', 'cats', 'badgers', 'ants']
Bei der Verwendung von sort() müssen Sie drei Dinge beachten. Erstens sortiert sort() die Liste unmittelbar. Versuchen Sie also nicht, den Rückgabewert zu erfassen, indem Sie Code wie spam = spam.sort() schreiben.
Zweitens können Sie keine Listen sortieren, in denen sowohl Zahlen als auch Strings vorkommen, da Python solche unterschiedlichen Werte nicht vergleichen kann. Bei dem folgenden Beispiel ergibt sich daher der Fehler TypeError:
>>> spam = [1, 3, 2, 4, 'Alice', 'Bob']
>>> spam.sort()
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
spam.sort()
TypeError: '<' not supported between instances of 'str' and 'int'
Drittens verwendet sort() statt der normalen alphabetischen Sortierung eine ASCII-Sortierung. Das bedeutet, dass alle Großbuchstaben vor den Kleinbuchstaben angeordnet werden. Ein kleines a steht daher hinter dem großen Z! Das können Sie an folgendem Beispiel erkennen:
>>> spam = ['Alice', 'ants', 'Bob', 'badgers', 'Carol', 'cats']
>>> spam.sort()
>>> spam
['Alice', 'Bob', 'Carol', 'ants', 'badgers', 'cats']
Wenn Sie die Werte in regulärer alphabetischer Reihenfolge sortieren müssen, übergeben Sie im Aufruf der Methode sort() den Wert str.lower für das Schlüsselwortargument key:
>>> spam = ['a', 'z', 'A', 'Z']
>>> spam.sort(key=str.lower)
>>> spam
['a', 'A', 'z', 'Z']
Dadurch behandelt sort() alle Elemente in der Liste so, als begännen sie mit einem Kleinbuchstaben, ohne sie aber tatsächlich zu ändern.
Reihenfolge der Listeneinträge mit reverse() umkehren
Wenn Sie die Reihenfolge der Einträge in einer Liste einfach nur umkehren müssen, rufen Sie dazu die Listenmethode reverse() auf:
>>> spam = ['cat', 'dog', 'moose']
>>> spam.reverse()
>>> spam
['moose', 'dog', 'cat']
Ebenso wie sort() gibt auch reverse() keine neue Liste zurück, weshalb Sie spam.reverse() schreiben müssen und nicht spam = spam.reverse().
Ausnahmen von den Einrückungsregeln in Python
In den meisten Fällen erkennt Python anhand der Tiefe der Einrückung, zu welchem Block eine Codezeile gehört. Es gibt jedoch einige Ausnahmen von dieser Regel. Eine davon sind Listen, die mehrere Zeilen einer Quellcodedatei umfassen können. Dabei spielt die Einrückung keine Rolle, denn Python weiß, dass die Liste erst mit der schließenden eckigen Klammer endet. Daher könnten Sie Ihren Code auch wie folgt schreiben:
spam = ['apples',
'oranges',
'bananas',
'cats']
print(spam)
In der Praxis sollten Sie Ihren Code so schreiben, dass die Listen klar und übersichtlich aussehen, etwa wie die Liste der Meldungen im Programm Magic 8 Ball.
Es ist auch möglich, eine Anweisung auf mehrere Zeilen aufzuteilen, indem Sie am Ende das Zeilenfortsetzungszeichen \ verwenden. Es bedeutet: »Diese Anweisung geht in der nächsten Zeile weiter.« Die Einrückung der Zeile, die auf das Zeichen \ folgt, spielt dann keine Rolle mehr. Beispielsweise ist folgender Code in Python gültig:
print('Four score and seven ' + \
'years ago...')
Diese Tricks sind praktisch, um lange Zeilen umzuarrangieren und Ihren Python-Code damit besser lesbar zu machen.
Beispielprogramm: Magic 8 Ball mithilfe einer Liste
Mithilfe von Listen können Sie das Programm Magic 8 Ball aus dem vorherigen Kapitel auf eine viel elegantere Weise schreiben. Anstatt mehrere Zeilen nahezu identischer elif-Anweisungen zu verwenden, können Sie eine einzelne Liste aufstellen, mit der der Code dann arbeitet. Geben Sie den folgenden Code im Dateieditor ein und speichern Sie ihn als magic8Ball2.py:
import random
messages = ['It is certain',
'It is decidedly so',
'Yes definitely',
'Reply hazy try again',
'Ask again later',
'Concentrate and ask again',
'My reply is no',
'Outlook not so good',
'Very doubtful']
print(messages[random.randint(0, len(messages) - 1)])
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/magic-8ball2/ ansehen.
Wenn Sie das Programm ausführen, werden Sie feststellen, dass es genauso funktioniert wie die ursprüngliche Version magic8Ball.py.
Beachten Sie den Ausdruck random.randint(0, len(messages) -1), den Sie hier als Index für messages verwenden. Dadurch wird für den Index eine Zufallszahl generiert, die von der Größe von messages abhängig ist – d. h., Sie erhalten eine Zufallszahl zwischen 0 und dem Wert von len(messages) - 1. Der Vorteil dieser Vorgehensweise besteht darin, dass Sie Elemente zur Liste messages hinzufügen und daraus entfernen können, ohne diese Codezeile zu ändern. Wenn Sie den Code später ändern, müssen Sie weniger Codezeilen anfassen, was auch weniger Gelegenheiten dafür bietet, Fehler einzubauen.
Sequenzielle Datentypen
Listen sind nicht die einzigen Datentypen, die eine geordnete Folge von Werten darstellen. So können Sie sich beispielsweise auch einen String als eine »Liste« von Textzeichen vorstellen. Zu den sequenziellen Datentypen von Python gehören Listen, Strings, die von range() zurückgegebenen Bereichsobjekte und Tupel (siehe den Abschnitt »Der Datentyp für Tupel« weiter hinten in diesem Kapitel). Viele der Dinge, die Sie mit Listen anstellen können, lassen sich auch mit Strings und anderen Werten sequenzieller Datentypen machen: Sie können sie indizieren, Sie können Slices daraus entnehmen, sie in for-Schleifen verwenden, ihre Größe mit len() bestimmen und die Operatoren in und not in anwenden. Geben Sie zur Veranschaulichung Folgendes in die interaktive Shell ein:
>>> name = 'Zophie'
>>> name[0]
'Z'
>>> name[-2]
'i'
>>> name[0:4]
'Zoph'
>>> 'Zo' in name
True
>>> 'z' in name
False
>>> 'p' not in name
False
>>> for i in name:
print('* * * ' + i + ' * * *')
* * * Z * * *
* * * o * * *
* * * p * * *
* * * h * * *
* * * i * * *
* * * e * * *
Veränderbare und unveränderbare Datentypen
Listen und Strings unterscheiden sich jedoch in einem sehr wichtigen Punkt: Eine Liste ist ein veränderbarer Datentyp, da Sie Elemente zu ihr hinzufügen, aus ihr entfernen und ändern können. Dagegen ist ein String unveränderbar – er lässt sich nicht ändern. Wenn Sie versuchen, ein einzelnes Zeichen in einem String neu zuzuweisen, erhalten Sie die Fehlermeldung TypeError, wie das folgende Beispiel zeigt:
>>> name = 'Zophie a cat'
>>> name[7] = 'the'
Traceback (most recent call last):
File "<pyshell#50>", line 1, in <module>
name[7] = 'the'
TypeError: 'str' object does not support item assignment
Um einen String zu »ändern«, müssen Sie ihn zerschneiden und verketten, um einen neuen String aus Teilen des alten zu erstellen. Probieren Sie das wie folgt in der interaktiven Shell aus:
>>> name = 'Zophie a cat'
>>> newName = name[0:7] + 'the' + name[8:12]
>>> name
'Zophie a cat'
>>> newName
'Zophie the cat'
Mit [0:7] und [8:12] haben wir die Zeichen angegeben, die nicht ersetzt werden sollen. Da Strings unveränderbar sind, wird der ursprüngliche String 'Zophie a cat' nicht geändert.
Eine Liste ist zwar veränderbar, dennoch nimmt die zweite Zeile im folgenden Code keine Änderung an der Liste eggs vor:
>>> eggs = [1, 2, 3]
>>> eggs = [4, 5, 6]
>>> eggs
[4, 5, 6]
In Wirklichkeit wird die Liste in eggs hier nicht geändert. Stattdessen wird die alte Liste [1, 2, 3] komplett mit der neuen Liste [4, 5, 6] überschrieben, wie Abbildung 4–2 deutlich zeigt.
Wenn Sie die ursprüngliche Liste in eggs tatsächlich so ändern wollen, dass sie schließlich [4, 5, 6] lautet, so müssen Sie wie folgt vorgehen:
>>> eggs = [1, 2, 3]
>>> del eggs[2]
>>> del eggs[1]
>>> del eggs[0]
>>> eggs.append(4)
>>> eggs.append(5)
>>> eggs.append(6)
>>> eggs
[4, 5, 6]
Abb. 4–2Wenn eggs = [4, 5, 6] ausgeführt wird, dann wird der Inhalt von eggs durch eine neue Liste ersetzt.
In diesem Beispiel steht in eggs am Ende noch die gleiche Liste wie zu Anfang; sie wurde lediglich geändert, aber nicht überschrieben. In Abb. 4–3 können Sie die sieben Schritte sehen, die in den ersten sieben Zeilen dieses Beispiels ablaufen.
Abb. 4–3Die Anweisung del und die Methode append() ändern den Listenwert unmittelbar.
Wenn Sie den Wert eines veränderbaren Datentyps ändern (z. B. wie in dem vorstehenden Beispiel mit der Anweisung del und der Methode append()), werden die Werte unmittelbar ausgetauscht. Der Wert der Variablen wird aber nicht durch einen neuen Listenwert ersetzt.
Die Unterscheidung zwischen veränderbaren und unveränderbaren Typen mag wie sinnlose Haarspalterei wirken, aber im Abschnitt »Verweise übergeben« weiter hinten in diesem Kapitel lernen Sie die unterschiedlichen Verhaltensweisen kennen, die sich je nachdem ergeben, ob Sie eine Funktion mit veränderbaren oder unveränderbaren Argumenten aufrufen. Bevor wir dorthin kommen, sehen wir uns jedoch noch den Datentyp für Tupel an, der eine unveränderbare Form des Listendatentyps darstellt.
Der Datentyp für Tupel
Der Datentyp für Tupel ist fast identisch mit dem für Listen, wobei jedoch zwei wichtige Unterschiede bestehen. Erstens werden Tupel in runden statt in eckigen Klammern angegeben, wie das folgende Beispiel zeigt:
>>> eggs = ('hello', 42, 0.5)
>>> eggs[0]
'hello'
>>> eggs[1:3]
(42, 0.5)
>>> len(eggs)
3
Der Hauptunterschied zwischen Listen und Tupeln besteht jedoch darin, dass Tupel ebenso wie Strings unveränderbar sind. Es ist nicht möglich, ihre Werte zu bearbeiten, zu entfernen oder neue Werte anzuhängen. Der folgende Code führt daher zu der Fehlermeldung TypeError:
>>> eggs = ('hello', 42, 0.5)
>>> eggs[1] = 99
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
eggs[1] = 99
TypeError: 'tuple' object does not support item assignment
Wenn sich in dem Tupel nur ein einziger Wert befindet, können Sie das dadurch kennzeichnen, dass Sie in den Klammern ein Komma hinter diesem Wert angeben. Anderenfalls würde Python davon ausgehen, dass Sie einfach nur einen Wert in Klammern geschrieben haben. Anhand des Kommas kann Python erkennen, dass es sich um einen Tupelwert handelt. (Im Gegensatz zu anderen Programmiersprachen ist es in Python zulässig, ein Komma hinter das letzte Element in einer Liste oder einem Tupel zu setzen.) Um den Unterschied zu erkennen, geben Sie in der interaktiven Shell folgende type()-Funktionen ein:
>>> type(('hello',))
<class 'tuple'>
>>> type(('hello'))
<class 'str'>
Mithilfe von Tupeln machen Sie für die Leser des Codes deutlich, dass diese Folge von Werten nicht geändert werden soll. Wenn Sie also eine geordnete Folge von Werten brauchen, die sich nie ändern, dann verwenden Sie dafür ein Tupel. Der zweite Vorteil von Tupeln gegenüber Listen besteht darin, dass Python aufgrund ihrer Unveränderbarkeit einige Optimierungen vornehmen kann, sodass der Code ein wenig schneller ausgeführt wird als bei der Verwendung von Listen.
Typen mit den Funktionen list() und tuple() umwandeln
So wie str(42) die Stringdarstellung '42' des Integerwertes 42 zurückgibt, so geben die Funktionen list() und tuple() die Listen- bzw. Tupelversionen der übergebenen Werte zurück. In den folgenden Beispielen weisen die Rückgabewerte einen anderen Datentyp auf als die übergebenen Werte:
>>> tuple(['cat', 'dog', 5])
('cat', 'dog', 5)
>>> list(('cat', 'dog', 5))
['cat', 'dog', 5]
>>> list('hello')
['h', 'e', 'l', 'l', 'o']
Die Umwandlung eines Tupels in eine Liste ist praktisch, wenn Sie eine veränderbare Version eines Tupelwertes benötigen.
Verweise
Wie Sie bereits wissen, lassen sich Strings und Integerwerte in Variablen »speichern«. Diese Beschreibung stellt jedoch eine Vereinfachung der wahren Vorgänge dar. In Wirklichkeit werden in den Variablen Verweise auf die Speicherorte festgehalten, an denen sich die Werte befinden. Geben Sie Folgendes in die interaktive Shell ein:
>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
Wenn Sie der Variablen spam den Wert 42 zuweisen, erstellen Sie in Wirklichkeit den Wert 42 im Arbeitsspeicher des Computers und speichern lediglich einen Verweis (oder eine Referenz) auf diesen Speicherort in der Variablen. Beim Zuweisen des Wertes in spam zu der Variablen cheese kopieren Sie diesen Verweis. Die Variablen spam und cheese verweisen jetzt beide auf den Wert 42 im Arbeitsspeicher. Wenn Sie den Wert in spam später auf 100 ändern, legen Sie einen neuen Wert 100 an und speichern einen Verweis darauf in spam. Das hat aber keinen Einfluss auf den Wert in cheese. Integer sind unveränderbare Werte. Wenn Sie die Variable spam ändern, lassen Sie sie in Wirklichkeit auf einen ganz anderen Wert im Arbeitsspeicher verweisen.
Listen funktionieren nicht auf diese Weise, da sie veränderbar sind. Der folgende Code macht diesen Unterschied deutlich:
>>> spam = [0, 1, 2, 3, 4, 5] 
>>> cheese = spam # Kopiert den Verweis, nicht die Liste 
>>> cheese[1] = 'Hello!' # Ändert den Listenwert 
>>> spam
[0, 'Hello!', 2, 3, 4, 5]
>>> cheese # Die Variable cheese verweist auf dieselbe Liste
[0, 'Hello!', 2, 3, 4, 5]
Das mag Ihnen merkwürdig vorkommen. Schließlich hat der Code nur die Liste cheese bearbeitet. Aber es scheint, dass beide Listen verändert worden sind!
Wenn Sie die Liste erstellen (
), weisen Sie der Variablen spam einen Verweis darauf zu. In der nächsten Zeile aber (
) wird nicht der Listenwert selbst in cheese kopiert, sondern nur der Listenverweis, der sich in spam befindet. Das bedeutet, dass die in spam und cheese gespeicherten Werte nun beide auf dieselbe Liste verweisen. Es gibt nur eine einzige zugrunde liegende Liste, da die Liste selbst gar nicht kopiert wurde. Wenn Sie daher das zweite Element von cheese ändern (
), bearbeiten Sie in Wirklichkeit dieselbe Liste, auf die auch spam verweist.
Gewöhnlich wird gesagt, dass Sie sich Variablen als Kisten vorstellen können, die Werte enthalten. Das ist hier jedoch nicht ganz korrekt, denn in Wirklichkeit enthalten die Variablen gar nicht die Listen selbst, sondern nur Verweise darauf. (Diese Verweise tragen ID-Nummern, die Python intern verwendet, aber darum brauchen wir uns nicht zu kümmern.) Abb. 4–4 zeigt, was geschieht, wenn eine Liste der Variablen spam zugewiesen wird.
Abb. 4–4Mit spam = [0, 1, 2, 3, 4, 5] wird ein Verweis auf die Liste gespeichert, aber nicht die Liste selbst.
In Abb. 4–5 sehen Sie, wie der Verweis in spam zu cheese kopiert wird. Das bedeutet aber nur, dass ein neuer Verweis erstellt und in cheese gespeichert wird, aber keine neue Liste. Beide verweisen auf dieselbe Liste.
Abb. 4–5Mit spam = cheese wird der Verweis kopiert, aber nicht die Liste.
Wenn Sie die Liste ändern, auf die cheese verweist, ändern Sie damit auch die Liste, auf die spam verweist, da es sich dabei um ein und dieselbe handelt. Das können Sie in Abb. 4–6 erkennen.
Abb. 4–6cheese[1] = 'Hello!' ändert die Liste, auf die beide Variablen verweisen.
Python-Variablen enthalten zwar eigentlich nur Verweise auf Werte, doch umgangssprachlich wird oft die Formulierung gebraucht, dass eine Variable einen Wert enthält.
Identität und die Funktion id()
Vielleicht fragen Sie sich, warum das merkwürdige Verhalten, das Sie im vorhergehenden Abschnitt kennengelernt haben, nicht auch bei unveränderbaren Werten wie Integern oder Strings vorkommt. Um das besser zu verstehen, können wir die Python-Funktion id() heranziehen. Alle Werte in Python haben eine eindeutige Identität, die Sie mit dieser Funktion abrufen können. Geben Sie folgenden Code in die interaktive Shell ein:
>>> id('Howdy') # Auf Ihrem Computer wird eine andere Zahl zurückgegeben
44491136
Wenn Python id('Howdy') ausführt, wird der String 'Howdy' im Arbeitsspeicher angelegt und die numerische Adresse seines Speicherorts zurückgegeben. Python wählt die Adresse danach aus, welche Bytes auf dem Computer gerade frei sind. Daher erhalten Sie jedes Mal, wenn Sie diesen Code ausführen, ein anderes Ergebnis.
Wie alle Strings ist auch 'Howdy' unveränderbar. Wenn Sie einen String in einer Variablen ändern, wird in Wirklichkeit ein neues Stringobjekt an einer anderen Stelle im Arbeitsspeicher angelegt und der Verweis darauf in der Variablen festgehalten. Mit dem folgenden Beispielcode können Sie beobachten, wie sich die Identität des Strings ändert, auf den bacon verweist:
>>> bacon = 'Hello'
>>> id(bacon)
44491136
>>> bacon += ' world!' # Erstellt neuen String aus 'Hello' und ' world!'
>>> id(bacon) # bacon verweist jetzt auf einen ganz anderen String
44609712
Listen dagegen sind veränderbar. Die Methode append() erstellt kein neues Listenobjekt, sondern verändert das vorhandene. Wir sprechen hier von einer unmittelbaren oder direkten Änderung des Objekts.
>>> eggs = ['cat', 'dog'] # Erstellt eine neue Liste
>>> id(eggs)
35152584
>>> eggs.append('moose') # append() ändert die Liste direkt
>>> id(eggs) # eggs verweist immer noch auf dieselbe Liste
35152584
>>> eggs = ['bat', 'rat', 'cow'] # Erstellt eine neue Liste mit neuer
Identität
>>> id(eggs) # eggs verweist jetzt auf eine ganz andere Liste
44409800
Wenn zwei Variablen auf dieselbe Liste verweisen (wie spam und eggs im vorherigen Abschnitt) und die Liste geändert wird, dann sind davon beide Variablen betroffen. Die Listenmethoden append(), extend(), remove(), sort(), reverse() usw. ändern Listen direkt.
Pythons automatische Garbage Collection entfernt alle Werte, auf die keine Variablen mehr verweisen, um Platz im Arbeitsspeicher freizumachen. Über diese Garbage Collection müssen Sie sich keine Gedanken machen, was eine gute Sache ist, denn durch die manuelle Speicherverwaltung in anderen Programmiersprachen schleichen sich häufig Fehler ein.
Verweise übergeben
Die Kenntnis von Verweisen ist unverzichtbar, um zu verstehen, wie Argumente an Funktionen übergeben werden. Bei einem Funktionsaufruf werden die Werte der Argumente in die Parametervariablen kopiert. Bei Listen als Argumenten (und bei Dictionaries, die ich im nächsten Kapitel beschreiben werde) bedeutet das, dass für den Parameter eine Kopie des Verweises verwendet wird. Um zu erkennen, was das bedeutet, geben Sie den folgenden Code im Dateieditor ein und speichern ihn als passingReference.py:
def eggs(someParameter):
someParameter.append('Hello')
spam = [1, 2, 3]
eggs(spam)
print(spam)
Beim Aufruf von eggs() wird kein Rückgabewert verwendet, um spam einen neuen Wert zuzuweisen. Stattdessen wird die Liste unmittelbar geändert. Dieses Programm führt zu folgender Ausgabe:
[1, 2, 3, 'Hello']
Obwohl spam und someParameter getrennte Verweise enthalten, verweisen doch beide auf dieselbe Liste. Daher wirkt sich der Methodenaufruf append('Hello') innerhalb der Funktion auch dann noch auf die Liste aus, wenn der Funktionsaufruf die Steuerung zurückgegeben hat.
Merken Sie sich dieses Verhalten. Wenn Sie nicht beachten, dass Python Listen- und Dictionary-Variablen auf diese Weise handhabt, kann das zu verwirrenden Fehlern führen.
Die Funktionen copy() und deepcopy() des Moduls copy
Verweise zu übergeben ist oft die sinnvollste Vorgehensweise, wenn eine Funktion tatsächlich die übergebene Liste oder das Dictionary bearbeiten soll. Es kann jedoch auch vorkommen, dass Sie die ursprüngliche Liste bzw. das Dictionary nicht ändern wollen. Für diesen Fall stellt Python das Modul copy bereit, das die Funktionen copy() und deepcopy() enthält. Mit copy.copy() können Sie eine echte Kopie eines veränderbaren Wertes wie einer Liste oder eines Dictionarys erstellen, also nicht einfach nur eine Kopie des Verweises darauf. Geben Sie folgenden Code in die interaktive Shell ein:
>>> import copy
>>> spam = ['A', 'B', 'C', 'D']
>>> id(spam)
57205555
>>> cheese = copy.copy(spam)
>>> id(cheese) # cheese ist eine andere Liste mit eigener Identität
57208888
>>> cheese[1] = 42
>>> spam
['A', 'B', 'C', 'D']
>>> cheese
['A', 42, 'C', 'D']
Hier verweisen die Variablen spam und cheese auf zwei unterschiedliche Listen, weshalb bei der Zuweisung von 42 zum Index 1 ausschließlich die Liste in cheese geändert wird. Wie Abb. 4–7 zeigt, verwenden die beiden Variablen nicht mehr dieselbe ID-Nummer, da sie nun auf verschiedene Listen verweisen.
Abb. 4–7cheese = copy.copy(spam) erstellt eine zweite Liste, die unabhängig von der ersten bearbeitet werden kann.
Wenn die Liste, die Sie kopieren wollen, selbst Listen enthält, verwenden Sie anstelle von copy.copy() die Funktion copy.deepcopy(). Damit werden auch die verschachtelten Listen kopiert.
Ein kurzes Programm: Conways Spiel des Lebens
Conways Spiel des Lebens ist ein zellulärer Automat, in dem das Verhalten eines aus diskreten Zellen bestehenden Spielfeldes durch einen Satz Regeln bestimmt wird. Außerdem erzeugt er eine recht hübsche Animation. Die einzelnen Schritte können Sie auch auf kariertes Papier zeichnen, wobei die Kästchen die Zellen darstellen. Ein ausgefülltes Kästchen »lebt«, ein leeres Kästchen ist »tot«. Eine lebende Zelle mit zwei oder drei lebenden Nachbarn lebt im nächsten Schritt weiter. Eine tote Zelle mit genau drei lebenden Nachbarn wird im nächsten Schritt lebendig. Alle anderen Zellen sterben im nächsten Schritt oder bleiben tot. Abb. 4–8 zeigt ein Beispiel für eine Folge von Schritten.
Abb. 4–8 Vier Schritte in Conways Spiel des Lebens
Trotz der einfachen Regeln treten viele überraschende Verhaltensweisen auf. In dem Spiel können sich Muster fortbewegen, selbst replizieren und sogar Computerprozessoren nachahmen. Die Grundlage für all diese komplexen Verhaltensweisen ist jedoch ein ziemlich einfaches Programm.
Das zweidimensionale Spielfeld können wir durch eine Liste aus Listen darstellen. Die inneren Listen sind dabei die einzelnen Spalten, wobei der String '#' für eine lebende Zelle steht und der String ' ' (also ein Leerzeichen) für eine tote. Geben Sie den folgenden Code in den Dateieditor ein und speichern Sie ihn als conway.py. Wenn Ihnen jetzt noch nicht alle Einzelheiten des Codes klar sein sollten, ist das kein Beinbruch. Geben Sie alles ein und versuchen Sie die Kommentare und Erklärungen so gut wie möglich nachzuvollziehen.
# Conways Spiel des Lebens
import random, time, copy
WIDTH = 60
HEIGHT = 20
# Erstellt eine Liste aus Listen für die Zellen
nextCells = []
for x in range(WIDTH):
column = [] # Erstellt eine neue Spalte
for y in range(HEIGHT):
if random.randint(0, 1) == 0:
column.append('#') # Fügt eine lebende Zelle hinzu
else:
column.append(' ') # Fügt eine tote Zelle hinzu
nextCells.append(column) # nextCells ist eine Liste aus Spaltenlisten
while True: # Hauptschleife des Programms
print('\n\n\n\n\n') # Trennt die einzelnen Schritte durch Zeilenumbrüche
currentCells = copy.deepcopy(nextCells)
# Gibt currentCells auf dem Bildschirm aus
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end='') # Gibt # oder Leerzeichen aus
print() # Gibt am Zeilenende einen Zeilenumbruch aus
# Berechnet die Zellen des nächsten Schritts anhand der aktuellen Zellen
for x in range(WIDTH):
for y in range(HEIGHT):
# Ruft die Koordinaten der Nachbarn ab
# Durch % WIDTH liegt leftCoord immer zwischen 0 und WIDTH - 1
leftCoord = (x - 1) % WIDTH
rightCoord = (x + 1) % WIDTH
aboveCoord = (y - 1) % HEIGHT
belowCoord = (y + 1) % HEIGHT
# Zählt die lebenden Nachbarn
numNeighbors = 0
if currentCells[leftCoord][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben links lebt
if currentCells[x][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben lebt
if currentCells[rightCoord][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben rechts lebt
if currentCells[leftCoord][y] == '#':
numNeighbors += 1 # Nachbarzelle links lebt
if currentCells[rightCoord][y] == '#':
numNeighbors += 1 # Nachbarzelle rechts lebt
if currentCells[leftCoord][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten links lebt
if currentCells[x][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten lebt
if currentCells[rightCoord][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten rechts lebt
# Bestimmt Zellenzustand nach den Regeln
if currentCells[x][y] == '#' and (numNeighbors == 2 or
numNeighbors == 3):
# Lebende Zelle mit 2 oder 3 lebenden Nachbarn lebt weiter
nextCells[x][y] = '#'
elif currentCells[x][y] == ' ' and numNeighbors == 3:
# Tote Zelle mit 3 Nachbarn wird lebendig
nextCells[x][y] = '#'
else:
# Alle anderen Zellen sterben oder bleiben tot
nextCells[x][y] = ' '
time.sleep(1) # Legt eine Pause von 1 s ein, um Flackern zu verhindern
Sehen wir uns diesen Code nun Zeile für Zeile an:
# Conways Spiel des Lebens
import random, time, copy
WIDTH = 60
HEIGHT = 20
Da wir die Funktionen random.randint.(), time.sleep() und copy.deepcopy() benötigen, importieren wir als Erstes die entsprechenden Module.
# Erstellt eine Liste aus Listen für die Zellen
nextCells = []
for x in range(WIDTH):
column = [] # Erstellt eine neue Spalte
for y in range(HEIGHT):
if random.randint(0, 1) == 0:
column.append('#') # Fügt eine lebende Zelle hinzu
else:
column.append(' ') # Fügt eine tote Zelle hinzu
nextCells.append(column) # nextCells ist eine Liste aus Spaltenlisten
Der erste Schritt unseres zellulären Automaten ist völlig willkürlich. Wir müssen eine Liste aus Listen erstellen, in denen wir die Strings '#' und ' ' zur Darstellung lebender und toter Zellen speichern, wobei die Position dieser Strings in den Listen die Position der entsprechenden Zellen auf dem Bildschirm bestimmt. Die inneren Listen stehen jeweils für eine Zellenspalte. Mit random.randint(0, 1) sorgen wir dafür, dass jede Zelle zu Anfang mit gleicher Wahrscheinlichkeit lebendig oder tot sein kann.
Diese Liste aus Listen stellen wir in die Variable nextCells, da der erste Schritt in der Hauptschleife des Programms darin besteht, nextCells in currentCells zu kopieren. In unserer Liste aus Listen wird die x-Koordinate bei 0 beginnend von links nach rechts gezählt und die y-Koordinate von oben nach unten. Daher entspricht nextCells[0][0] der Zelle in der oberen linken Ecke, nextCells[1][0] der Zelle rechts daneben und nextCells[0][1] der Zelle darunter.
while True: # Hauptschleife des Programms
print('\n\n\n\n\n') # Trennt die einzelnen Schritte durch Zeilenumbrüche
currentCells = copy.deepcopy(nextCells)
Jede Iteration der Hauptschleife bildet einen Schritt des zellulären Automaten. Dabei kopieren wir jeweils nextCells in currentCells, geben currentCells auf dem Bildschirm aus und berechnen dann anhand der Zellen in currentCells die Zellen in nextCells.
# Gibt currentCells auf dem Bildschirm aus
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end='') # Gibt # oder Leerzeichen aus
print() # Gibt am Zeilenende einen Zeilenumbruch aus
Mit diesen verschachtelten for-Schleifen geben wir jeweils eine komplette Zeilenreihe auf dem Bildschirm aus, gefolgt von einem Zeilenumbruch am Ende. Dieser Vorgang wird für jede Zeile in currentCells wiederholt.
# Berechnet die Zellen des nächsten Schritts anhand der aktuellen Zellen
for x in range(WIDTH):
for y in range(HEIGHT):
# Ruft die Koordinaten der Nachbarn ab
# Durch % WIDTH liegt leftCoord immer zwischen 0 und WIDTH - 1
leftCoord = (x - 1) % WIDTH
rightCoord = (x + 1) % WIDTH
aboveCoord = (y - 1) % HEIGHT
belowCoord = (y + 1) % HEIGHT
Als Nächstes brauchen wir zwei verschachtelte for-Schleifen, um die einzelnen Zellen für den nächsten Schritt zu berechnen. Ob eine Zelle lebendig oder tot ist, hängt von ihren Nachbarn ab, weshalb wir als Erstes die Indizes der Zellen links, rechts, über und unter der aktuellen x- und y-Koordinate bestimmen müssen.
Mit dem Modulo-Operator % können wir auch den Umlauf an den Rändern berücksichtigen. Die äußerste linke Spalte hat den Index 0, der linke Nachbar einer Zelle in dieser Spalte hat also rein rechnerisch den Index 0 - 1 oder -1. Um stattdessen auf den Index der äußersten rechten Spalte zu kommen, nämlich 59, rechnen wir (0 - 1) % WIDTH. Da WIDTH den Wert 60 hat, wird dieser Ausdruck zu 59 ausgewertet. Diese Umlauftechnik mit dem Modulo-Operator funktioniert ebenso auch für die rechten, oberen und unteren Nachbarn.
# Zählt die lebenden Nachbarn
numNeighbors = 0
if currentCells[leftCoord][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben links lebt
if currentCells[x][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben lebt
if currentCells[rightCoord][aboveCoord] == '#':
numNeighbors += 1 # Nachbarzelle oben rechts lebt
if currentCells[leftCoord][y] == '#':
numNeighbors += 1 # Nachbarzelle links lebt
if currentCells[rightCoord][y] == '#':
numNeighbors += 1 # Nachbarzelle rechts lebt
if currentCells[leftCoord][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten links lebt
if currentCells[x][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten lebt
if currentCells[rightCoord][belowCoord] == '#':
numNeighbors += 1 # Nachbarzelle unten rechts lebt
Um zu bestimmen, ob die Zelle in nextCells[x][y] lebendig oder tot sein soll, müssen wir die lebenden Nachbarzellen von currentCells[x][y] zählen. Die vorstehende Folge von if-Anweisung prüft jeden der acht Nachbarn der Zelle und addiert für jeden lebenden 1 zu numNeighbors.
# Bestimmt Zellenzustand nach den Regeln
if currentCells[x][y] == '#' and (numNeighbors == 2 or
numNeighbors == 3):
# Lebende Zelle mit 2 oder 3 lebenden Nachbarn lebt weiter
nextCells[x][y] = '#'
elif currentCells[x][y] == ' ' and numNeighbors == 3:
# Tote Zelle mit 3 Nachbarn wird lebendig
nextCells[x][y] = '#'
else:
# Alle anderen Zellen sterben oder bleiben tot
nextCells[x][y] = ' '
time.sleep(1) # Legt eine Pause von 1 s ein, um Flackern zu verhindern
Damit wissen wir nun, wie viele lebende Nachbarn die Zelle in currentCells[x] [y] hat, sodass wir nextCells[x][y] dementsprechend auf '#' oder ' ' setzen können. Nachdem wir alle möglichen x- und y-Koordinaten durchlaufen haben, lassen wir das Programm durch den Aufruf von time.sleep(1) eine Pause von einer Sekunde einlegen. Danach geht die Programmausführung zum Anfang der Hauptschleife zurück, um mit dem nächsten Schritt weiterzumachen.
Je nach Ausgangsverteilung der Zellen können verschiedene bekannte Muster auftreten, für die Namen wie »Gleiter«, »Propeller« oder »schweres Raumschiff« geprägt wurden. Das in Abb. 4–8 gezeigte Gleitermuster bewegt sich alle vier Schritte in diagonaler Richtung fort. Um einen einzelnen Gleiter zu erzeugen, ersetzen Sie die folgende Zeile in conway.py:
if random.randint(0, 1) == 0:
durch diese Zeile:
if (x, y) in ((1, 0), (2, 1), (0, 2), (1, 2), (2, 2)):
Wenn Sie mehr über die faszinierenden Muster erfahren möchten, die Sie erzeugen können, suchen Sie im Web nach »Conways Spiel des Lebens« oder »Conway’s Game of Life«. Ähnliche Python-Programme wie das hier vorgestellte finden Sie auf https://github.com/asweigart/pythonstdiogames.
Zusammenfassung
Listen bilden einen sehr nützlichen Datentyp, da Sie damit Code schreiben können, der eine beliebige Anzahl von Werten in einer einzigen Variablen bearbeiten kann. Weiter hinten in diesem Buch werden Sie Programme kennenlernen, in denen Listen für Aufgaben eingesetzt werden, die ohne sie nur sehr schwer oder überhaupt nicht zu erledigen wären.
Listen gehören zu den sequenziellen Datentypen und sind veränderbar, d. h., ihr Inhalt kann sich ändern. Tupel und Strings dagegen, die zwar auch zu den sequenziellen Datentypen zählen, sind unveränderbar. Eine Variable, die ein Tupel oder einen String enthält, kann mit einem neuen Tupel oder String überschrieben werden, aber das ist nicht das Gleiche wie die unmittelbare Veränderung eines Wertes, wie sie beispielsweise mit den Methoden append() und remove() für Listen möglich ist.
In Variablen werden Listen nicht direkt gespeichert, sondern nur in Form von Verweisen auf die Listen. Dieser Unterschied ist von großer Bedeutung, wenn Variablen kopiert oder Listen als Argumente an Funktionsaufrufe übergeben werden. Da der kopierte Wert nur der Verweis auf die Liste ist, können sich jegliche Änderungen, die Sie an der Liste vornehmen, auch auf andere Variablen in dem Programm auswirken. Wenn Sie eine Liste in einer Variablen bearbeiten wollen, ohne dabei die ursprüngliche Liste zu ändern, müssen Sie sie mit copy() oder deepcopy() kopieren.
Wiederholungsfragen
1 Was ist []?
2 Wie weisen Sie den Wert 'hello' als drittes Element der Liste zu, die in der Variablen spam gespeichert ist? (Nehmen Sie an, spam enthält [2, 4, 6, 8, 10].)
Nehmen Sie für die folgenden drei Fragen an, dass spam die Liste ['a','b', 'c', 'd'] enthält.
1 3.Wozu wird spam[int('3' * 2) // 11] ausgewertet?
2 4.Wozu wird spam[-1] ausgewertet?
3 5.Wozu wird spam[:2] ausgewertet?
Nehmen Sie für die folgenden drei Fragen an, dass bacon die Liste [3.14, 'cat', 11, 'cat', True] enthält.
1 6.Wozu wird bacon.index('cat') ausgewertet?
2 7.Wie sieht der Listenwert in bacon nach der Ausführung von bacon.append(99) aus?
3 8.Wie sieht der Listenwert in bacon nach der Ausführung von bacon.remove('cat') aus?
4 9.Wie sehen die Operatoren für die Listenverkettung und die Listenwiederholung aus?
5 10.Was ist der Unterschied zwischen den Listenmethoden append() und insert()?
6 11.Welche beiden Möglichkeiten gibt es, um Elemente aus einer Liste zu entfernen?
7 12.Nennen Sie einige Ähnlichkeiten zwischen Listen- und Stringwerten!
8 13.Worin unterscheiden sich Listen und Tupel?
9 14.Wie geben Sie einen Tupelwert an, der nur den Integerwert 42 enthält?
10 15.Wie können Sie die Tupelform eines Listenwertes erzeugen? Wie die Listenform eines Tupelwertes?
11 16.In Variablen, die Listenwerte »enthalten«, ist die eigentliche Liste gar nicht gespeichert. Was enthalten sie in Wirklichkeit?
12 17.Was ist der Unterschied zwischen copy.copy() und copy.deepcopy()?
Übungsprojekte
Schreiben Sie zur Übung Programme, die die folgenden Aufgaben erledigen.
Kommacode
Nehmen Sie an, Sie haben einen Listenwert wie den folgenden:
spam = ['apples', 'bananas', 'tofu', 'cats']
Schreiben Sie eine Funktion, die einen Listenwert als Argument entgegennimmt und einen String zurückgibt, in dem alle Listenelemente durch ein Komma und ein Leerzeichen getrennt sind und vor dem letzten Eintrag and steht. Wenn Sie beispielsweise die vorstehende Liste spam an diese Funktion übergeben, sollte sie 'apples, bananas, tofu, and cats' zurückgeben. Die Funktion muss allerdings bei jedem beliebigen Listenwert funktionieren. Prüfen Sie insbesondere den Fall, dass der Funktion eine leere Liste (also []) übergeben wird.
Münzwurffolgen
In dieser Übung sollen Sie ein Experiment durchführen. Wenn Sie hundertmal eine Münze werfen und dabei jeweils K für Kopf und Z für Zahl notieren, erhalten Sie eine Liste wie »Z Z Z Z K K K K Z Z«. Soll sich aber jemand eine Folge von einhundert zufälligen Münzwürfen ausdenken, ist das Ergebnis gewöhnlich eher etwas wie »K Z K Z K K Z K Z Z«, was für Menschen zwar zufällig aussieht, aber nicht zufällig im mathematischen Sinne ist. Ein Mensch wird sich praktisch nie eine Serie von sechs Kopf- oder Zahlwürfen hintereinander ausdenken, obwohl so etwas bei zufälligen Münzwürfen sehr wahrscheinlich ist. Menschen sind nicht gut darin, zufällige Muster zu erzeugen.
Schreiben Sie ein Programm, um herauszufinden, wie oft in einer zufällig generierten Folge von simulierten Münzwürfen sechsmal Kopf oder sechsmal Zahl hintereinander erscheinen. Im ersten Teil muss das Programm also eine Liste zufällig ausgewählter Kopf- und Zahlwerte anlegen und im zweiten Teil überprüfen, ob eine Serie darin vorkommt. Stellen Sie den gesamten Code in eine Schleife, die das Experiment 10.000 Mal durchführt, sodass Sie auch ermitteln können, in welchem Prozentsatz der Münzwürfe sich eine Serie von sechsmal Kopf oder sechsmal Zahl ergibt. Kleiner Tipp: Die Funktion random.randint(0, 1) gibt die Werte 0 und 1 mit gleicher Wahrscheinlichkeit zurück.
Verwenden Sie die folgende Struktur als Ausgangspunkt:
import random
numberOfStreaks = 0
for experimentNumber in range(10000):
# Code, der eine Liste von 100 Kopf- oder Zahlwerten erstellt
# Code der prüft, ob Kopf oder Zahl sechsmal hintereinander vorkommt
print('Chance of streak: %s%%' % (numberOfStreaks / 100))
Das Ergebnis ist natürlich nur ein Schätzwert, allerdings bilden 10.000 Experimente schon eine recht gute Stichprobe. Mit Kenntnissen der Wahrscheinlichkeitsrechnung könnten Sie die genaue Antwort bestimmen, ohne erst ein Programm schreiben zu müssen, aber Programmierer sind nun mal bekanntermaßen schlecht in Mathematik.
Zeichenbildraster
Gegeben sei eine Liste von Listen, bei der jedes Element der inneren Listen ein String aus einem Zeichen ist:
grid = [['.', '.', '.', '.', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['O', 'O', 'O', 'O', 'O', '.'],
['.', 'O', 'O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'O', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['.', '.', '.', '.', '.', '.']]
Sie können sich grid[x][y] als das Zeichen an der x- und y-Koordinate eines Bildes vorstellen, das mithilfe von Textzeichen dargestellt wird. Der Ursprung (0, 0) liegt dabei in der oberen linken Ecke, die x-Koordinate wird nach rechts gezählt, die y-Koordinate nach unten.
Kopieren Sie den oben angegebenen Listenwert grid und schreiben Sie Code, der damit das folgende Bild ausgibt:
..OO.OO..
.OOOOOOO.
.OOOOOOO.
..OOOOO..
...OOO...
....O....
Tipp: Sie brauchen eine Schleife innerhalb einer Schleife. Als Erstes geben Sie grid[0][0], grid[1][0], grid[2][0] usw. bis grid[8][0] aus. Damit ist die erste Zeile vollständig, sodass Sie danach einen Zeilenumbruch einschalten. Anschließend muss das Programm grid[0][1], grid[1][1], grid[2][1] usw. ausgeben. Als Letztes wird grid[8][5] ausgegeben.
Denken Sie daran, das Schlüsselwortargument end an print() zu übergeben, um zu verhindern, dass nach jedem Aufruf von print() automatisch ein Zeilenumbruch erfolgt.
5
Dictionaries und Datenstrukturen
In diesem Kapitel geht es um den Datentyp für Dictionaries (Wörterbücher), der eine flexible Möglichkeit bietet, um Daten zu gliedern und darauf zuzugreifen. Durch die Kombination von Dictionaries mit den aus dem letzten Kapitel bekannten Listen sind Sie in der Lage, Datenstrukturen zu erstellen, um beispielsweise ein Tic-Tac-Toe-Spielfeld zu modellieren.
Der Datentyp für Dictionaries
Ebenso wie Listen sind Dictionaries Zusammenstellungen mehrerer Werte. Die Indizes in Dictionaries können aber nicht nur Integer sein wie bei Listen, sondern auch andere Datentypen aufweisen. Sie werden Schlüssel genannt, und die Kombination eines Schlüssels mit dem zugehörigen Wert heißt Schlüssel-Wert-Paar.
Dictionaries werden mithilfe von geschweiften Klammern geschrieben:
>>> myCat = {'size': 'fat', 'color': 'gray', 'disposition': 'loud'}
Mit dieser Codezeile wird der Variablen myCat ein Dictionary zugewiesen. Die Schlüssel in diesem Dictionary sind 'size', 'color' und 'disposition' mit den Werten 'fat', 'gray' und 'loud'. Mithilfe der Schlüssel können Sie die Werte abrufen:
>>> myCat['size']
'fat'
>>> 'My cat has ' + myCat['color'] + ' fur.'
'My cat has gray fur.'
In Dictionaries können auch Integer als Schlüssel verwendet werden. Allerdings müssen Sie im Gegensatz zu den Indizes von Listen nicht bei null beginnen, sondern können ganz beliebig sein:
>>> spam = {12345: 'Luggage Combination', 42: 'The Answer'}
Dictionaries und Listen im Vergleich
Im Gegensatz zu Listen sind Dictionaries nicht geordnet. Das erste Element in der Liste spam ist spam[0], aber in einem Dictionary gibt es kein erstes Element. Bei der Frage, ob zwei Listen gleich sind, spielt die Reihenfolge der Elemente eine Rolle, aber bei einem Dictionary ist es ohne Bedeutung, in welcher Reihenfolge die Schlüssel-Wert-Paare stehen. Geben Sie zur Veranschaulichung Folgendes in die interaktive Shell ein:
>>> spam = ['cats', 'dogs', 'moose']
>>> bacon = ['dogs', 'moose', 'cats']
>>> spam == bacon
False
>>> eggs = {'name': 'Zophie', 'species': 'cat', 'age': '8'}
>>> ham = {'species': 'cat', 'age': '8', 'name': 'Zophie'}
>>> eggs == ham
True
Da Dictionaries ungeordnet sind, können Sie auch keine Slices daraus entnehmen.
Wenn Sie versuchen, auf einen Schlüssel zuzugreifen, den es in dem Dictionary nicht gibt, erhalten Sie die Fehlermeldung KeyError, vergleichbar mit dem IndexError, der sich ergibt, wenn Sie bei einer Liste einen nicht existierenden Index abfragen. Im folgenden Beispiel erscheint diese Fehlermeldung, da es den Schlüssel 'color' nicht gibt:
>>> spam = {'name': 'Zophie', 'age': 7}
>>> spam['color']
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
spam['color']
KeyError: 'color'
Dictionaries sind zwar ungeordnet, aber da Sie darin willkürliche Werte als Schlüssel verwenden können, stellen sie eine äußerst vielseitige und nützliche Möglichkeit dar, um Daten zu strukturieren. Nehmen wir an, Sie möchten in einem Programm die Geburtsdaten Ihrer Freunde speichern. Dazu können Sie ein Dictionary verwenden, bei dem die Namen dieser Personen die Schlüssel und die Geburtsdaten die Werte sind. Geben Sie den folgenden Code im Dateieditor ein und speichern Sie ihn als birthdays.py:
birthdays = {'Alice': 'Apr 1', 'Bob': 'Dec 12', 'Carol': 'Mar 4'} 
while True:
print('Enter a name: (blank to quit)')
name = input()
if name == '':
break
if name in birthdays: 
print(birthdays[name] + ' is the birthday of ' + name) 
else:
print('I do not have birthday information for ' + name)
print('What is their birthday?')
bday = input()
birthdays[name] = bday 
print('Birthday database updated.')
Die Ausführung dieses Programms können Sie sich auf https//autbor.com/bdaydb/ ansehen. Als Erstes erstellen Sie ein Basis-Dictionary und speichern es in birthdays (
). Ob ein Name bereits in dem Dictionary vorhanden ist, können Sie (ebenso wie bei Listen) mit dem Schlüsselwort in herausfinden (
). Wenn der Name schon gespeichert ist, können Sie den zugehörigen Wert mithilfe von eckigen Klammern abrufen (
). Wenn nicht, verwenden Sie die gleiche Schreibweise mit eckigen Klammern und den Zuweisungsoperator, um den Namen und einen Wert hinzuzufügen (
).
Die Ausgabe dieses Programms sieht wie folgt aus:
Enter a name: (blank to quit)
Alice
Apr 1 is the birthday of Alice
Enter a name: (blank to quit)
Eve
I do not have birthday information for Eve
What is their birthday?
Dec 5
Birthday database updated.
Enter a name: (blank to quit)
Eve
Dec 5 is the birthday of Eve
Enter a name: (blank to quit)
Am Ende des Programms werden jedoch alle eingegebenen Daten verworfen. Wie Sie Daten in Dateien auf Ihrer Festplatte speichern, erfahren Sie in Kapitel 9.
»Geordnete« Dictionaries in Python 3.7
Auch in Python 3.7 sind Dictionaries nicht im eigentlichen Sinne geordnet und haben auch kein »erstes« Schlüssel-Wert-Paar, allerdings merkt sich Python die Reihenfolge, in der Sie die Paare eingefügt haben, für den Fall, dass Sie einen sequenziellen Wert aus dem Dictionary erzeugen wollen. Im folgenden Beispiel treten die Einträge in den Listen, die aus den Dictionaries eggs und ham erstellt wurden, in derselben Reihenfolge auf, in der sie jeweils in die Dictionaries aufgenommen wurden:
>>> eggs = {'name': 'Zophie', 'species': 'cat', 'age': '8'}
>>> list(eggs)
['name', 'species', 'age']
>>> ham = {'species': 'cat', 'age': '8', 'name': 'Zophie'}
>>> list(ham)
['species', 'age', 'name']
Die Dictionaries sind jedoch nach wie vor ungeordnet, sodass Sie nicht mit Indizes wie eggs[0] oder ham[2] auf die Einträge zugreifen können. Außerdem sollten Sie sich nicht auf das neue Verhalten verlassen, da sich Dictionaries in älteren Python-Versionen die Einfügereihenfolge der Schlüssel-Wert-Paare nicht merken. Wenn ich also den folgenden Code in Python 3.5 ausführe, stimmt die Reihenfolge der Einträge nicht mit der Einfügereihenfolge überein:
>>> spam = {}
>>> spam['first key'] = 'value'
>>> spam['second key'] = 'value'
>>> spam['third key'] = 'value'
>>> list(spam)
['first key', 'third key', 'second key']
Die Methoden keys(), values() und items()
Dictionaries verfügen über drei Methoden, die listenähnliche Werte mit den Schlüsseln, den Werten bzw. den Schlüssel-Wert-Paaren zurückgeben, nämlich keys(), values() und items(). Die Rückgabewerte sind jedoch keine echten Listen, denn sie können nicht verändert werden und haben auch keine append()-Methode. Die Datentypen dieser Werte (dict_keys, dict_values und dict_items) können jedoch in for-Schleifen verwendet werden. Um zu sehen, wie das funktioniert, geben Sie Folgendes in die interaktive Shell ein:
>>> spam = {'color': 'red', 'age': 42}
>>> for v in spam.values():
... print(v)
red
42
Hier durchläuft die for-Schleife alle Werte im Dictionary spam. Es ist jedoch auch eine Iteration über alle Schlüssel oder alle Schlüssel-Wert-Paare möglich:
>>> for k in spam.keys():
... print(k)
color
age
>>> for i in spam.items():
... print(i)
('color', 'red')
('age', 42)
Unter Verwendung der Methoden keys(), values() und items() kann eine for-Schleife also über die Schlüssel, die Werte oder die Schlüssel-Wert-Paare eines Dictionarys iterieren. Beachten Sie, dass die von der Methode items() zurückgegebenen dict_items-Werte Tupel aus Schlüssel und Wert sind.
Wenn Sie aus diesen Methoden echte Listen gewinnen möchten, übergeben Sie die listenähnlichen Rückgabewerte an die Funktion list():
>>> spam = {'color': 'red', 'age': 42}
>>> spam.keys()
dict_keys(['color', 'age'])
>>> list(spam.keys())
['color', 'age']
In der Zeile list(spam.keys()) wird der von keys() zurückgegebene dict_keys-Wert an die Funktion list() übergeben, die den Listenwert ['color', 'age'] zurückgibt.
In der for-Schleife können Sie auch eine Mehrfachzuweisung durchführen, um Schlüssel und Wert unterschiedlichen Variablen zuzuweisen:
>>> spam = {'color': 'red', 'age': 42}
>>> for k, v in spam.items():
... print('Key: ' + k + ' Value: ' + str(v))
Key: age Value: 42
Key: color Value: red
Das Vorhandensein eines Schlüssels oder Wertes im Dictionary ermitteln
Im vorigen Kapitel haben Sie gelernt, dass Sie mit den Operatoren in und not in prüfen können, ob ein gegebener Wert in einer Liste vorhanden ist. Mithilfe dieser Operatoren lässt sich jedoch auch ermitteln, ob ein bestimmter Schlüssel oder Wert in einem Dictionary existiert:
>>> spam = {'name': 'Zophie', 'age': 7}
>>> 'name' in spam.keys()
True
>>> 'Zophie' in spam.values()
True
>>> 'color' in spam.keys()
False
>>> 'color' not in spam.keys()
True
>>> 'color' in spam
False
Die Schreibweise 'color' in spam in diesem Beispiel ist im Grunde genommen eine Abkürzung für 'color' in spam.keys(). Das können Sie immer so machen: Wenn Sie prüfen wollen, ob ein Schlüssel in einem Dictionary vorkommt (oder nicht), wenden Sie das Schlüsselwort in (bzw. not in) einfach auf das Dictionary an.
Die Methode get()
Es wäre ziemlich mühsam, wenn man beim Abruf des Wertes für einen Schlüssel in einem Dictionary zunächst immer prüfen müsste, ob dieser Schlüssel überhaupt vorhanden ist. Zum Glück gibt es für Dictionaries die Methode get(), die zwei Argumente annimmt, nämlich den Schlüssel des abzurufenden Wertes und einen Standardwert, der zurückgegeben werden soll, wenn es den Schlüssel nicht gibt.
Geben Sie Folgendes in die interaktive Shell ein:
>>> picnicItems = {'apples': 5, 'cups': 2}
>>> 'I am bringing ' + str(picnicItems.get('cups', 0)) + ' cups.'
'I am bringing 2 cups.'
>>> 'I am bringing ' + str(picnicItems.get('eggs', 0)) + ' eggs.'
'I am bringing 0 eggs.'
Da im Dictionary picnicItems kein Schlüssel namens 'eggs' vorhanden ist, gibt die Methode get() den Standardwert 0 zurück. Hätten Sie die Abfrage ohne die Methode get() versucht, so wäre eine Fehlermeldung die Folge gewesen:
>>> picnicItems = {'apples': 5, 'cups': 2}
>>> 'I am bringing ' + str(picnicItems['eggs']) + ' eggs.'
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
'I am bringing ' + str(picnicItems['eggs']) + ' eggs.'
KeyError: 'eggs'
Die Methode setdefault()
Es kommt häufig vor, dass Sie in einem Dictionary nach einem Schlüssel suchen und ihm nur dann einen Wert hinzufügen wollen, wenn er noch keinen hat. Das können Sie mit folgendem Code erreichen:
spam = {'name': 'Pooka', 'age': 5}
if 'color' not in spam:
spam['color'] = 'black'
Mithilfe der Methode setdefault() können Sie das in einer einzigen Codezeile erledigen. Das erste Argument dieser Methode ist der Schlüssel, nach dem Sie suchen, das zweite der Wert, der dafür festgelegt werden soll, falls der Schlüssel noch nicht existiert. Ist der Schlüssel schon vorhanden, so gibt setdefault() dessen bestehenden Wert zurück. Das können Sie sich ansehen, indem Sie folgenden Code in die interaktive Shell eingeben:
>>> spam = {'name': 'Pooka', 'age': 5}
>>> spam.setdefault('color', 'black')
'black'
>>> spam
{'color': 'black', 'age': 5, 'name': 'Pooka'}
>>> spam.setdefault('color', 'white')
'black'
>>> spam
{'color': 'black', 'age': 5, 'name': 'Pooka'}
Beim ersten Aufruf von setdefault() wird das Dictionary in spam in {'color': 'black', 'age': 5, 'name': 'Pooka'} geändert. Die Methode gibt 'black' zurück, da dies der Wert ist, der für den Schlüssel 'color' festgelegt wurde. Wird danach spam.setdefault('color', 'white') aufgerufen, wird der Wert des Schlüssels 'color' jedoch nicht in 'white' geändert, da dieser Schlüssel bereits vorhanden ist.
Die Methode setdefault() stellt eine praktische Abkürzung dar, um sicherzustellen, dass ein Schlüssel vorhanden ist. Das folgende kurze Programm zählt, wie oft die einzelnen Zeichen in einem String vorkommen. Geben Sie den folgenden Code im Dateieditor ein und speichern Sie ihn als characterCount.py:
message = 'It was a bright cold day in April, and the clocks were striking
thirteen.'
count = {}
for character in message:
count.setdefault(character, 0) 
count[character] = count[character] + 1 
print(count)
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/setdefault ansehen. Es durchläuft alle Zeichen im String der Variablen message und zählt, wie oft sie jeweils vorkommen. Der Aufruf der Methode setdefault() (
) sorgt dafür, dass der Schlüssel character (mit dem Standardwert 0) im Dictionary count vorhanden ist, damit das Programm bei der Ausführung von count[character] = count[character] + 1 (
) keinen KeyError hervorruft. Die Ausgabe dieses Programms sieht wie folgt aus:
{' ': 13, ',': 1, '.': 1, 'A': 1, 'I': 1, 'a': 4, 'c': 3, 'b': 1, 'e': 5,
'd': 3, 'g': 2, 'i':6, 'h': 3, 'k': 2, 'l': 3, 'o': 2, 'n': 4, 'p': 1,
's': 3, 'r': 5, 't': 6, 'w': 2, 'y': 1}
Hieraus können Sie ablesen, dass das kleine c dreimal vorkommt, das Leerzeichen 13 Mal und das große A einmal. Dieses Programm funktioniert unabhängig davon, wie der String in der Variablen message aussieht, selbst wenn er eine Million Zeichen lang ist!
Saubere Ausgabe
Wenn Sie das Modul pprint importieren, haben Sie Zugriff auf die Funktionen pprint() und pformat(), mit denen Sie ein Dictionary sauber ausgeben können (»pretty print«). Das ist hilfreich, wenn Sie eine übersichtlichere Anzeige der Elemente in einem Dictionary benötigen, als print() sie bietet. Ändern Sie das Programm characterCount.py wie folgt ab und speichern Sie es als prettyCharacterCount.py:
import pprint
message = 'It was a bright cold day in April, and the clocks were striking
thirteen.'
count = {}
for character in message:
count.setdefault(character, 0)
count[character] = count[character] + 1
pprint.pprint(count)
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/pprint/ ansehen. Die Ausgabe ist nach Schlüsseln geordnet und viel übersichtlicher:
{' ': 13,
',': 1,
'.': 1,
'A': 1,
'I': 1,
-- schnipp --
't': 6,
'w': 2,
'y': 1}
Die Funktion pprint.pprint() ist insbesondere dann hilfreich, wenn das Dictionary selbst wiederum Listen oder andere Dictionaries als Werte enthält.
Wenn Sie den aufgehübschten Text nicht auf dem Bildschirm ausgeben, sondern in einen Stringwert umwandeln möchten, rufen Sie stattdessen pprint.pformat() auf. Die folgenden beiden Codezeilen sind gleichwertig:
pprint.pprint(someDictionaryValue)
print(pprint.pformat(someDictionaryValue))
Datenstrukturen zur Modellierung realer Objekte
Schon bevor es das Internet gab, war es möglich, mit einer Person Schach zu spielen, die sich am anderen Ende der Welt befand. Beide Spieler stellten dabei jeweils ein Schachbrett bei sich zu Hause auf und sandten sich abwechselnd Postkarten mit ihren Spielzügen zu. Dazu brauchten die Spieler natürlich eine eindeutige Möglichkeit, um die Züge zu beschreiben.
In der algebraischen Schachnotation werden die Felder auf dem Schachbrett durch Koordinaten aus Zahlen und Buchstaben bezeichnet, wie Sie in Abb. 5–1 sehen.
Abb. 5–1 Die Koordinaten auf einem Schachbrett in algebraischer Schachnotation
Auch die Figuren werden durch Buchstaben bezeichnet: K für König, D für Dame, T für Turm, S für Springer, L für Läufer. Die Beschreibung des Zugs einer Partei (Halbzug) besteht aus dem Buchstaben für die Figur und den Zielkoordinaten. Ein Paar solcher Halbzüge gibt an, was bei einem kompletten Zug geschieht (wobei Weiß vorn steht). So bedeutet 2. Sf3 Sc6, dass Weiß im zweiten Zug einen seiner Springer auf Feld f3 gezogen hat und Schwarz einen Springer auf Feld c6.
Damit ist die algebraische Notation natürlich noch nicht komplett beschrieben. Wichtig für unsere Zwecke ist jedoch, dass es damit möglich ist, eine Schachpartie eindeutig zu beschreiben, ohne dass die beiden Spieler am selben Brett sitzen müssen; sie können sich durchaus auf verschiedenen Kontinenten befinden. Wenn Sie ein gutes Gedächtnis haben, brauchen Sie nicht einmal ein echtes Schachbrett, sondern können einfach den Spielzug auf der Postkarte lesen und den Spielstand im Kopf aktualisieren.
Computer haben ein gutes Gedächtnis. Ein Programm auf einem modernen Rechner kann problemlos Milliarden von Zügen wie '2. Sf3 Sc6' speichern. Dadurch können Computer Schach spielen, ohne auf ein physisches Schachbrett zurückzugreifen. Sie modellieren Daten, um das Schachbrett darzustellen. Der Code arbeitet dann mit diesem Modell.
Dabei kommen Listen und Dictionaries ins Spiel. Beispielsweise kann das Dictionary {'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'} das Schachbrett aus Abb. 5–2 darstellen.
Abb. 5–2Das Schachbrett, das durch das Dictionary {'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'} modelliert wird.
Als Beispiel wählen wir jedoch ein einfacheres Spiel als Schach, nämlich Tic-Tac-Toe.
Ein Tic-Tac-Toe-Brett
Ein Tic-Tac-Toe-Brett sieht wie ein überdimensioniertes Doppelkreuz (#) mit neun Feldern aus, die jeweils ein X, ein O oder gar nichts enthalten können. Um ein solches Brett durch ein Dictionary darzustellen, weisen Sie jedem Feld einen Schlüssel in Form eines Strings zu, wie in Abb. 5–3 zu sehen.
Abb. 5–3 Die Felder eines Tic-Tac-Toe-Bretts mit den zugehörigen Schlüsseln
Was sich auf den einzelnen Feldern des Bretts befindet, können Sie ebenfalls durch Stringwerte darstellen: 'X', 'O' oder ' ' (ein Leerzeichen für ein leeres Feld). Insgesamt müssen Sie neun dieser Stringwerte speichern. Dazu verwenden Sie ein Dictionary. Der Stringwert mit dem Schlüssel 'top-R' steht für den Inhalt des Feldes in der rechten oberen Ecke, der Stringwert mit dem Schlüssel 'low-L' für das Feld unten links, der Stringwert mit dem Schlüssel 'mid-M' für das mittlere Feld usw.
Dieses Dictionary ist eine Datenstruktur, die ein Tic-Tac-Toe-Brett darstellt. Speichern Sie es in der Variablen theBoard. Geben Sie dazu den folgenden Quellcode im Dateieditor ein und speichern Sie ihn als ticTacToe.py:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
Die Datenstruktur in der Variablen theBoard steht für das Tic-Tac-Toe-Brett aus Abb. 5–4.
Abb. 5–4 Ein leeres Tic-Tac-Toe-Brett
Da die Werte zu den Schlüsseln in theBoard jeweils Strings aus einem Leerzeichen sind, stellt dieses Dictionary ein komplett leeres Brett dar. Wenn der X-Spieler anfängt und sein Kreuz im mittleren Feld macht, wird das Brett anschließend durch folgendes Dictionary repräsentiert:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
Die Datenstruktur in theBoard stellt jetzt das Tic-Tac-Toe-Brett aus Abb. 5–5 dar.
Abb. 5–5 Der erste Zug
Wenn der O-Spieler gewinnt, nachdem er die obere Reihe komplett ausgefüllt hat, sieht das Brett wie folgt aus:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
Diese Datenstruktur steht für das Brett mit dem Spielstand aus Abb. 5–6.
Abb. 5–6Spieler O gewinnt.
Die Spieler können jedoch nicht den Inhalt der Variablen sehen, sondern nur das, was auf dem Bildschirm ausgegeben wird. Daher schreiben wir eine Funktion, die das Dictionary für das Brett anzeigt. Ergänzen Sie ticTacToe.py wie folgt (der neu hinzugekommene Code ist fett dargestellt):
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
printBoard(theBoard)
Die Ausführung dieses Programms können Sie sich auf https:/autbor.com/tictactoe1/ ansehen. Wenn Sie es starten, gibt printBoard() das leere Tic-Tac-Toe-Brett aus:
| |
-+-+-
| |
-+-+-
| |
Die Funktion printBoard() kann mit jeder Tic-Tac-Toe-Datenstruktur umgehen, die Sie ihr übergeben. Versuchen Sie den Code wie folgt abzuändern:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M':
'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
printBoard(theBoard)
Die Ausführung dieses Programms können Sie sich auf https:/autbor.com/tictactoe2/ ansehen. Auf dem Bildschirm erscheint das Spielbrett jetzt wie folgt:
O|O|O
-+-+-
X|X|
-+-+-
| |X
Sie haben eine Datenstruktur erstellt, die das Tic-Tac-Toe-Brett wiedergibt, und in printBoard() Code geschrieben, der diese Struktur interpretiert. Damit haben Sie ein Programm, das das Spielbrett modelliert. Die Datenstruktur hätten Sie auch anders gestalten können (z. B. durch Schlüssel wie 'TOP-LEFT' statt 'top-L'), aber solange der Code mit der Datenstruktur umgehen kann, funktioniert das Programm richtig.
Beispielsweise erwartet die Funktion printBoard(), dass die Tic-Tac-Toe-Datenstruktur ein Dictionary mit Schlüsseln für alle neun Felder ist. Wenn dem Dictionary, das dieser Funktion übergeben wird, ein Schlüssel fehlt, beispielsweise 'mid-L', funktioniert das Programm nicht mehr:
O|O|O
-+-+-
Traceback (most recent call last):
File "ticTacToe.py", line 10, in <module>
printBoard(theBoard)
File "ticTacToe.py", line 6, in printBoard
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
KeyError: 'mid-L'
Als Nächstes fügen wir Code hinzu, mit dem die Spieler ihre Züge eingeben können. Ändern Sie das Programm ticTacToe.py wie folgt ab:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M':
' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
turn = 'X'
for i in range(9):
printBoard(theBoard) 
print('Turn for ' + turn + '. Move on which space?')
move = input() 
theBoard[move] = turn 
if turn == 'X': 
turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
Die Ausführung dieses Programms können Sie sich auf https:/autbor.com/tictactoe3/ ansehen. Der neu hinzugefügte Code gibt zu Beginn jedes Zugs das Brett aus (
), ruft dann den Zug des Spielers ab, der gerade an der Reihe ist (
), aktualisiert daraufhin das Spielbrett (
) und wechselt dann zum anderen Spieler (
), bevor der nächste Zug erfolgt.
Die Ausgabe sieht wie folgt aus:
| |
-+-+-
| |
-+-+-
| |
Turn for X. Move on which space?
mid-M
| |
-+-+-
|X|
-+-+-
| |
-- schnipp --
O|O|X
-+-+-
X|X|O
-+-+-
O| |X
Turn for X. Move on which space?
low-M
O|O|X
-+-+-
X|X|O
-+-+-
O|X|X
Dies ist natürlich noch kein komplettes Programm für ein Tic-Tac-Toe-Spiel, denn unter anderem wird nicht geprüft, welcher Spieler gewonnen hat. Es reicht aber aus, um Ihnen zu zeigen, wie Sie Datenstrukturen in Ihren Programmen einsetzen können.
Hinweis
Den Quellcode für ein vollständiges Tic-Tac-Toe-Programm finden Sie in dem Begleitmaterial, das Sie von www.dpunkt.de/python_automatisieren_2/ herunterladen können.
Verschachtelte Dictionaries und Listen
Die Modellierung eines Tic-Tac-Toe-Bretts ist ziemlich einfach, denn dazu brauchen Sie nur ein einziges Dictionary mit neun Schlüssel-Wert-Paaren. Wenn Sie kompliziertere Dinge modellieren wollen, kann es jedoch sein, dass Sie dazu Dictionaries und Listen benötigen, die andere Dictionaries und Listen enthalten. Listen eignen sich, wenn Sie eine geordnete Folge von Werten brauchen, und Dictionaries, wenn Sie Werte mit Schlüsseln verknüpfen möchten. In dem folgenden Programm wird ein Dictionary verwendet, das selbst wiederum Dictionaries enthält, um festzuhalten, wer was zu einem Picknick mitbringt. Die Funktion totalBrought() kann diese Datenstruktur lesen und berechnen, in welcher Gesamtmenge ein Nahrungsmittel oder Gegenstand von den Gästen mitgebracht wird.
allGuests = {'Alice': {'apples': 5, 'pretzels': 12},
'Bob': {'ham sandwiches': 3, 'apples': 2},
'Carol': {'cups': 3, 'apple pies': 1}}
def totalBrought(guests, item):
numBrought = 0
for k, v in guests.items(): 
numBrought = numBrought + v.get(item, 0) 
return numBrought
print('Number of things being brought:')
print(' - Apples ' + str(totalBrought(allGuests, 'apples')))
print(' - Cups ' + str(totalBrought(allGuests, 'cups')))
print(' - Cakes ' + str(totalBrought(allGuests, 'cakes')))
print(' - Ham Sandwiches ' + str(totalBrought(allGuests, 'ham sandwiches')))
print(' - Apple Pies ' + str(totalBrought(allGuests, 'apple pies')))
Die Ausführung dieses Programms können Sie sich auf https:/autbor.com/guestpicnic/ ansehen. In der Funktion totalBrought() durchläuft die for-Schleife die Schlüssel-Wert-Paare von guests (
). In der Schleife wird der String für den Namen des Gastes zu k zugewiesen und das Dictionary der Dinge, die er mitbringt, zu v. Ist der Parameter eines Elements schon als Schlüssel in dem Dictionary vorhanden, wird sein Wert (die Menge) zu numBrought addiert (
). Gibt es den Schlüssel noch nicht, gibt die Methode get() den Wert 0 zurück, der dann zu numBrought addiert wird.
Die Ausgabe dieses Programms sieht wie folgt aus:
Number of things being brought:
- Apples 7
- Cups 3
- Cakes 0
- Ham Sandwiches 3
- Apple Pies 1
Die Modellierung scheint so einfach zu sein, dass Sie sich vielleicht gar nicht erst die Mühe machen wollen, ein Programm dafür zu schreiben. Denken Sie aber daran, dass die Funktion totalBrought() auch problemlos Dictionaries mit Tausenden von Gästen handhaben kann, von denen jeder wiederum Tausende von Dingen zum Picknick mitbringt. Wenn Sie all diese Informationen in einer Datenstruktur haben und mit der Funktion totalBrought() verarbeiten können, sparen Sie eine Menge Zeit!
Mit Datenstrukturen können Sie reale Objekte auf beliebige Weise modellieren, solange der Rest des Programms korrekt mit diesem Datenmodell umgehen kann. Wenn Sie mit dem Programmieren anfangen, sollten Sie sich zunächst nicht allzu viele Gedanken darüber machen, was die »richtige« Art und Weise der Modellierung ist. Je mehr Erfahrung Sie gewinnen, umso effizientere Modelle werden Sie erstellen. Das Wichtigste ist, dass das Datenmodell für die Bedürfnisse des Programms geeignet ist.
Zusammenfassung
In diesem Kapitel haben Sie Dictionaries kennengelernt. Listen und Dictionaries sind Werte, die mehrere andere Werte enthalten, bei denen es sich auch wiederum um Listen und Dictionaries handeln kann. Der Clou bei Dictionaries besteht darin, dass Sie damit einen Eintrag (den Schlüssel) einem anderen (dem Wert) zuordnen können, wohingegen Listen lediglich eine geordnete Folge von Werten enthalten. Um auf die Werte innerhalb eines Dictionarys zuzugreifen, verwenden Sie ebenso wie bei Listen die Schreibweise mit eckigen Klammern. Anstelle eines Integerindex werden bei Dictionaries Schlüssel genutzt, die verschiedene Datentypen aufweisen können – Integer, Fließkommazahlen, Strings und Tupel. Durch die Gliederung von Werten in Datenstrukturen können Sie reale Objekte modellieren. Das haben Sie am Beispiel des Tic-Tac-Toe-Bretts gesehen.
Wiederholungsfragen
1 Wie sieht der Code für ein leeres Dictionary aus?
2 Wie sieht ein Dictionary mit dem Schüssel 'foo' und dem Wert 42 aus?
3 Was ist der Hauptunterschied zwischen einem Dictionary und einer Liste?
4 Was geschieht, wenn Sie versuchen, spam['foo'] abzurufen und spam den Wert {'bar': 100} hat?
5 Was ist der Unterschied zwischen 'cat' in spam und 'cat' in spam.keys(), wenn in spam ein Dictionary gespeichert ist?
6 Was ist der Unterschied zwischen 'cat' in spam und 'cat' in spam.values(), wenn in spam ein Dictionary gespeichert ist?
7 Wie können Sie den folgenden Code kürzer schreiben?if 'color' not in spam:spam['color'] = 'black'
8 Welche Module und Funktionen können Sie verwenden, um Dictionaries auf saubere und übersichtliche Weise auszugeben?
Übungsprojekte
Schreiben Sie zur Übung Programme, die die folgenden Aufgaben erledigen.
Validierer für Schach-Dictionary
In diesem Kapitel haben wir mit dem Dictionary {'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'} ein Schachbrett dargestellt. Schreiben Sie die Funktion isValidChessBoard(), die ein Dictionary als Argument entgegennimmt und je nachdem, ob es sich um ein zulässiges Schachbrett handelt oder nicht, True oder False ausgibt.
Auf einem zulässigen Schachbrett befinden sich genau ein schwarzer und ein weißer König. Jeder Spieler kann höchstens 16 Figuren habe, darunter höchstens acht Bauern, und alle Figuren müssen sich auf einer gültigen Position zwischen '1a' und '8h' befinden, also nicht z. B. auf '9z'. Die Namen der Figuren beginnen je nachdem, ob sie weiß oder schwarz sind, mit 'w' oder 'b', worauf die Bezeichnung 'pawn', 'knight', 'bishop', 'rook', 'queen' oder 'king' folgt. Diese Funktion soll erkennen können, ob ein Fehler zu einem unzulässigen Schachbrett geführt hat.
Inventar für ein Fantasyspiel
Stellen Sie sich vor, Sie schreiben ein Fantasy-Computerspiel. Als Datenstruktur für das Inventar des Spielers verwenden Sie ein Dictionary. Die Schlüssel sind dabei Strings, die die einzelnen Posten in dem Inventar beschreiben, während der Wert ein Integer ist, der angibt, wie viele dieser Gegenstände der Spieler hat. Beispielsweise bedeutet der Dictionary-Wert {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}, dass der Spieler über ein Seil, sechs Fackeln, 42 Goldmünzen, einen Dolch und zwölf Pfeile verfügt.
Schreiben Sie nun eine Funktion namens displayInventory(), die ein beliebiges Inventar entgegennimmt und wie folgt anzeigt:
Inventory:
12 arrow
42 gold coin
1 rope
6 torch
1 dagger
Total number of items: 62
Tipp: Um die Schlüssel in einem Dictionary zu durchlaufen, können Sie eine for-Schleife verwenden.
# inventory.py
stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}
def displayInventory(inventory):
print("Inventory:")
item_total = 0
for k, v in inventory.items():
# FÜLLEN SIE DIESEN TEIL SELBST AUS
print("Total number of items: " + str(item_total))
displayInventory(stuff)
Eine Funktion zum Hinzufügen von Listeninhalten zum Inventar-Dictionary
Nehmen Sie an, in einem Spiel wird der Hort eines besiegten Drachens wie folgt als Liste von Strings dargestellt:
dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']
Schreiben Sie eine Funktion namens addToInventory(inventory, addedItems), wobei der Parameter inventory das Dictionary mit dem Inventar eines Spielers (wie im vorherigen Projekt) ist und der Parameter addedItems eine Liste wie dragonLoot.
Die Funktion addToInventary() soll ein Dictionary zurückgeben, das für das aktualisierte Inventar steht. Beachten Sie, dass die Liste addedItems mehrere Vorkommen des gleichen Gegenstands enthalten kann. Ihr Code sollte folgenden allgemeinen Aufbau haben:
def addToInventory(inventory, addedItems):
# Bauen Sie hier Ihren eigenen Code ein
inv = {'gold coin': 42, 'rope': 1}
dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']
inv = addToInventory(inv, dragonLoot)
displayInventory(inv)
Das vorstehende Programm sollte (mit der Funktion displayInventory() aus dem vorherigen Projekt) Folgendes ausgeben:
Inventory:
45 gold coin
1 rope
1 ruby
1 dagger
Total number of items: 48
The free sample has ended.