Die Programmiersprache Python

Einführung mit der Turtle

Allgemeines zur Turtle

Turtle-Grafik wurde eigens für pädagogische Zwecke von Seymour Papert im Rahmen der Programmiersprache LOGO erdacht.
Das Konzept der Turtle-Grafik war so erfolgreich, dass es oft kopiert wurde. In vielen Programmiersprachen gibt es ein Turtle-Modul, auch in Python.

Papert war der Begründer des Konstruktionismus.

Konstruktionismus

Der Konstruktionismus ist eine Lerntheorie, welche die Bedeutung des aktiven Handelns für den Lernprozess betont. Sie ist von den Ideen des Konstruktivismus inspiriert. Wie der Konstruktivismus geht auch der Konstruktionismus davon aus, dass Wissen durch die Lernenden selbst aufgebaut (re-konstruiert) werden muss und nicht einfach vermittelt werden kann. Darüber hinaus betrachtet der Konstruktionismus besonders das Herstellen (Konstruieren) von Artefakten, sei es die Gestaltung eines Produkts, der Bau einer Sandburg oder das Schreiben eines Computerprogramms als sehr lernförderlich.

papert

"The role of the teacher is to create the conditions for invention rather than provide ready-made knowledge"

"The goal is to teach in such a way as to produce the most learning from the least teaching"

"I am convinced that the best learning takes place when the learner takes charge"

Von Papert wurde auch die große Bedeutung des Peer learning betont.

Grundidee

turtle
Turtle: Ort, Orientierung, Stift
Turtle wird über ein Programm gesteuert
Man kann den Programmen bei der Arbeit gleichsam "zusehen".

Hier einige wichtige Turtle-Befehle (Auswahl):
fd(Strecke) (forward)Die Schildkröte bewegt sich um "Strecke" nach vorne.
bk(Strecke) (back)Die Schildkröte bewegt sich um "Strecke" rückwärts.
lt(Winkel) (left)Die Schildkröte dreht sich um "Winkel" nach links.
rt(Winkel) (right)Die Schildkröte dreht sich um "Winkel" nach rechts.
pu() (pen up)Die Schildkröte hebt den Zeichenstift an (und schreibt nicht mehr).
pd() (pen down)Die Schildkröte senkt den Zeichenstift (und schreibt wieder).
pensize(Breite)setzt die Stiftdicke auf "Breite".
pencolor(Farbe)setzt die Stiftfarbe auf "Farbe".
shape(Form)setzt die Form der Schildkröte (‘arrow’,‘classic’,‘turtle’,‘circle’).
home()Schildkröte kehrt nach (0,0) zurück (Mitte des Zeichenblatts).
clear()Löschen der Zeichnung, Schildkröte ändert Zustand nicht.
reset()Löschen der Zeichnung, Schildkröte geht in Ausgangszustand.
setup(Breite, Hoehe)Fenster mit "Breite", "Hoehe" erzeugen.
heading()In welche Richtung schaut die Schildkröte? (3-Uhr-Position = 0)
setheading(Winkel)Drehe die Schildkröte in Richtung "Winkel" (3-Uhr-Position=0).
goto(x, y)Bewege die Turtle zur Position x, y.
(Falls pen=down wird auch gezeichnet. Ändert die Orientierung nicht.)
dot(size, color)Zeichnet einen Kreis der Größe "size" und der Farbe "color" an der aktuellen Position.
towards(x, y)Liefert den Winkel, den sich die Schildkröte drehen müsste um auf x, y zu zeigen.
xcor(), ycor()Liefern die x bzw y Koordinate der Turtle

Hier findest Du alle Befehle auf einen Blick (oder als pdf zum Ausdrucken).

Motivation

coord spiro mandel koch interaktiv

turtle.zip
Oben ganz rechts: interaktive_turtle_graphiken.zip

Allgemeines zu Python

Python ist eine moderne, leicht zu erlernende Programmiersprache.
Installation und weitere Hintergründe sind hier erklärt.

Installation

Installation der 32-bit Version der Standardinstallation für Python 3 der Python-Foundation (python.org).
Häkchen setzen: "Python zum Pfad hinzufügen"

Shell und IDLE

Die Python-Shell ist sehr praktisch um Ausdrücke oder Anweisungen zu testen und Fehler darin zu finden.
Programme schreib aber am besten immer in IDLE und leg vorher mit
File > New File
eine neue Programmdatei an. Wähle nicht unbedingt das vorgeschlagene Verzeichnis, sondern ein Verzeichnis, wo Du die Dateien leicht wieder findet.
Achtung!: Niemals ein Skript so nennen wie ein Modul, das man importiert. Das führt zu schwierig zu findenden Fehlern.
Also speziell kein Programm "turtle.py" benennen.

Hello World

Traditionsgemäß beginnen wir mit dem "Hello World"-Programm.

# _hello_world.py # Anweisung, Modul-Import, Kommentar # eingebaute Funktion mit Parameter # Funktion aus Modul mit Parameter # Zeichenketten-Literal from turtle import * # importiere alles aus dem Modul Turtle write("hello world!") print("hello again!") done() # Manche IDEs erwarten am Ende des Programms diesen Befehl

Ausgabe:
>>> hello again! >>>

Ergebnis:
01

Erklärung:
Der print-Befehl erzeugt eine Ausgabe in der Shell (Konsole). Der write-Befehl führt zu einer Ausgabe auf der Zeichenfläche.

Anweisungen

# _anweisung.py # Ganzzahl-Literal from turtle import * # importiere alles aus dem Modul Turtle fd(50) # 50 Einheiten nach vorn

Ergebnis:
01

Was sind Anweisungen?

Befehle (= Funktionen oder eingebaute Funktionen, siehe Liste), Variablenzuweisungen, ...
Eine Anweisung erstreckt sich über eine oder mehrere Zeilen.

Anweisungsfolgen

# _anweisungsfolge.py # Anweisungsfolge from turtle import * fd(50) lt(90) fd(50)

Ergebnis:
02

Einfache Programme können lediglich aus einer Anweisungsfolge bestehen.
Diese Anweisungsfolge wird in der vorliegenden Reihenfolge abgearbeitet.

Hier ein weiteres Beispiel:

# _anweisungsfolge.py from turtle import * fd(50) lt(90) fd(50) rt(90) fd(50) lt(90) fd(50) rt(90) fd(50)

Ergebnis:
03

Noch ein Beispiel:

# _anweisungsfolge.py from turtle import * pensize(3) fd(150) bk(150) lt(10) fd(150) bk(150)

Ergebnis:
02

Übungen

Schleifen

for-Schleife

# _for.py # for-Schleife, range-Funktion from turtle import * speed(0) pensize(3) for i in range(10): fd(150) bk(150) lt(10)

Ergebnis:
04

Erklärung:
Hier siehst du das erste mal einen Programmblock. Dieser wird in Python mit einer Einrückung durchgeführt. Alle Zeilen die eingerückt sind gehören zum selben Block. Die erste Zeile die nicht eingerückt ist, gehört nicht mehr zum Block.
for-Schleifen verwenden oft die range-Funktion. Der Aufruf von range(n) erzeugt eine Serie von 0 bis n-1. Das kann man auch in einer Shell-Sitzung so ausprobieren:

>>> print(list(range(5))) [0, 1, 2, 3, 4] >>>

Die Variable i nennt man den Schleifenzähler. Traditionell wird dafür gern der Buchstabe i (für Index) verwendet aber zum Beispiel wären auch count, zaehler oder jeder andere gültige Variablennamen (siehe später) sind hier in Ordnung.

while-Schleife

# _for_while.py # while-Schleife from turtle import * speed(0) pensize(3) pu() bk(200) pd() for i in range(10): fd(150) bk(150) lt(10) pu() setheading(0) fd(250) # --------- dasselbe nochmal mit while: pd() i = 10 while i > 0: fd(150) bk(150) lt(10) i -= 1

Ergebnis:
04

Übungen

Wie oben:
Aber verwende eine Schleife.
Verwende eine Schleife und zeichne folgendes:
beispiel schleife

Variable

# _variable.py # Variable, Zuweisung, Zeichenketten-Literal, "+="-Schreibweise from turtle import * pensize(3) speed(0) len = 10 color("blue") for i in range(36): fd(len) bk(len) lt(10) len += 5

Ergebnis:
04

Erklärung:
Variablen sind "Behälter" für Werte. Diese Werte können dann über den Variablennamen angesprochen werden. Eine Variable behält ihren Wert bis ihr ein neuer Wert zugewiesen wird.

Gültigkeitsregeln für Variablennamen

Gute Variablennamen drücken die Bedeutung/Verwendung des Werts aus, den sie enthalten. Zum Beispiel:
anzahl, summe, laenge, vorname, nachname, alter, temperatur_12_uhr, ...
strecke, winkel, entfernung, x, y, z (für Koordinaten)
...

Per Konvention (PEP 8) werden Variablen- und Funktionsnamen in Python klein geschrieben. (Selbstgeschriebene Klassen beginnen mit einem Großbuchstaben).

Übungen

Mithilfe einer Variablen zeichne folgendes:
beispiel variable

if-Anweisung

Erst if-Anweisungen ermöglichen es dem Programm auf innere Zustände oder Eingaben von außen unterschiedlich zu reagieren.

Zur Wiederholung:

# _for.py from turtle import * pensize(3) speed(0) for i in range(36): fd(150) bk(150) lt(10)

Ergebnis:
04

Alternativ dazu:
# _if.py # if-Anweisung from turtle import * pensize(3) speed(0) color("blue") for i in range(36): if i>18: color("red") fd(150) bk(150) lt(10)

Ergebnis:
04

Erklärung:
Die if-Anweisung überprüft in jedem Schleifendurchlauf die Zählvariable. Ist diese größer als 18, dann schaltet die Turtle die Farbe auf Rot. Statt "if i>18:" hätte "if i==19:" das gleiche Ergebnis gebracht. (Mit gleichem Ablauf?)

else-Zweig

Sehr häufig hat man die Situation, dass man bedingte Anweisungen immer dann ausführen will wenn die if-Bedingung nicht erfüllt ist. Dafür gibt es den else-Zweig:

# _if_else.py # else-Zweig, Modulo-Operator from turtle import * pensize(3) speed(0) for i in range(36): if i%2==0: # True für gerade Zahlen color("red") else: color("black") fd(150) bk(150) lt(10)

Ergebnis:
04

if- und else-Zweig dürfen jeweils nur einmal in der if-Anweisung vorkommen.

elif-Zweig

Anstatt ein if-Statement in einen else-Zweig einzubetten ist ein elif zu bevorzugen.

# _if_elif_else.py # elif-Zweig from turtle import * pensize(3) speed(0) for i in range(36): if i%2==0: # True für gerade Zahlen color("red") elif i%3==0: color("orange") else: color("black") fd(150) bk(150) lt(10)

Ergebnis:
04

Erklärung:
Die Reihenfolge der Überprüfungen ist wichtig. Im obigen Fall setzt sich die Farbe der geraden Zahlen durch gegen die Farbe der durch 3 teilbaren. Das könnte man umkehren, indem man die beiden Bedingungen austauscht. Insbesondere ist man selbst dafür zuständig, eine sinnvolle Reihenfolge und sinnvolle Einschränkungen vorzunehmen. Man wird hier nicht auf logische Denkfehler hingewiesen zum Beispiel wenn man eine Bedingung abfragt, die gar nicht erfüllt sein kann, weil eine Einschränkung weiter oben den Fall bereits abgehandelt hat. Im obigen Fall, zum Beispiel, hätte die zusätzliche Abfrage "elif i%4==0:" nach dem elif-Zweig keinen Sinn mehr, denn der if-Zweig fängt alle Fälle bereits ab, die man hier erwarten würde.

Der elif-Zweig darf mehrfach vorkommen:

# _if_elif_else.py from turtle import * pensize(25) speed(0) colormode(255) for i in range(36): if i > 30: color("lightgrey") elif i > 25: color("pink") elif i > 20: color("violet") elif i > 15: color("purple") elif i > 10: color("blue") elif i > 5: color("turquoise") else: color("lightblue") fd(150) bk(150) lt(10)

Ergebnis:
04

Hier noch ein weiteres Beispiel:

# _if_elif_else.py from turtle import * pensize(25) speed(0) colormode(255) for i in range(36): if i%5==4: #color("red") #color("#ff0000") color(255,0,0) elif i%5==3: color(205,0,0) elif i%5==2: color(155,0,0) elif i%5==1: color(105,0,0) elif i%5==0: color(55,0,0) else: color("grey") fd(150) bk(150) lt(10)

Ergebnis:
04

Erklärung:
Beachte hier am Beispiel für den Rotwert, die verschiedenen Möglichkeiten, eine Farbe anzugeben:
Der else-Zweig wird in diesem Programm nie erreicht (=toter Code). Trotzem ist es manchmal vorteilhaft solche Überprüfungen zu belassen, denn die Situation kann sich ändern sobald jemand das Programm ändert. (Zum Beispiel Änderung aller "i%5" auf "i%6".)

Datentypen

Man unterscheidet grundlegende (atomare) Datentypen: und höhere: Serien, zusammengesetzt

# _datentypen.py # Grundlegende Datentypen: Zeichenkette, Ganzzahl, # Fließkommazahl, Boolean # Zeitausgabe from turtle import * from time import * speed(0) show_time = True color("blue") for i in range(100): fd(150) bk(150) lt(3.6) if show_time: pu() goto(0,180) color("red") write(strftime("%H:%M", localtime()))

Ergebnis:
04

Erklärung:
Dieses Programm wurde so erstellt, dass alle 4 grundlegenden Datentypen gemeinsam vertreten sind:
Beachte auch die Ermittlung der Systemzeit durch Aufruf der strftime-Funktion (www.strftime.org).

Ausdrücke

# _ausdruck.py # Ausdruck, alternative Import-Variante, Math-Modul # Methodenschreibweise, Operatorenrangfolge # setup-Methode # Exponent-Operator: ** from turtle import * import math setup(500,500) seite = 150 fd(seite) lt(90) fd(seite) lt(135) fd(math.sqrt(2*seite**2)) # Ausdruck (Expression)

Ergebnis:
05

Übungen

# _zwischenpunkt.py # Übung: vervollständige das Programm # mehrzeiliger Kommentar from turtle import * setup(500,500) x1=20 y1=0 x2=140 y2=190 pu() goto(x1, y1) dot(15, "blue") goto(x2, y2) dot(15, "blue") """ # Zeichne genau zwischen den beiden blauen Punkten einen roten Punkt: goto(..., ...) dot(15, "red") """

Ergebnis:

09
Lösung: goto((x1+x2)/2,(y1+y2)/2)

Listen

Listen sind ein wichtiger Vertreter höherer (nicht atomarer) Datenstrukturen.
Diese werden für Serien und zusammengesetzte Werte verwendet.
Auf Elemente in der Liste wird mittels einem sogenannten Index zugegriffen.
Aber Achtung!: Der Index geht von 0 bis (Listenlänge - 1).

# _liste_1.py # Liste from turtle import * from random import * pensize(7) speed(0) colors = ["red", "orange", "blue", "green"] for i in range(4): color(colors[i]) fd(100) bk(100) lt(10)

Ergebnis:
Liste 1

Erklärung:
colors[0] liefert "red"
colors[1] liefert "orange"
colors[2] liefert "blue"
colors[3] liefert "green"

Das kann man so auch in der Shell ausprobieren:

>>> colors = ["red", "orange", "blue", "green"] >>> colors[3] 'green' >>> colors[-1] 'green' >>> colors[4] Traceback (most recent call last): File "", line 1, in IndexError: list index out of range >>> colors[-2] 'blue' >>>

Erklärung:
Eine Überschreitung des Index liefert eine Fehlermeldung, Angabe von -1, -2, ... darf aber verwendet werden um auf das letzte, das vorletzte Element, ... zuzugreifen.

Zeichnen wir nun wieder den gesamten Kreis diesmal mit zufälligen Farben:

# _liste_2.py # Liste, random Modul, Zufallszahlen from turtle import * from random import * pensize(7) speed(0) colors = ["red", "orange", "blue", "green"] for i in range(36): color(choice(colors)) fd(100) bk(100) lt(10)

Ergebnis:
Liste 2

Erklärung:
Mittels der Funktion "choice" aus dem random-Modul wird ein zufälliges Listenelement zurückgeliefert. Hier sieht man sehr gut die Philosophie von Python: in anderen Sprachen würde man immer über den Index arbeiten und den zufällig erzeugen, aber erinnern wir uns zurück, was passiert wenn man über das Ende der Liste hinausläuft. Wenn also ein Fehler in der Generierung eine zu hohe Zahl generiert, dann kommt es im Programm zum Laufzeitfehler.

Machen wir auch die Länge der Strahlen zufällig:

# _liste_3.py # Liste, random Modul, Zufallszahlen from turtle import * from random import * pensize(7) speed(0) colors = ["red", "orange", "blue", "green"] for i in range(36): color(choice(colors)) len = randrange(50, 150) fd(len) bk(len) lt(10)

Ergebnis:
Liste 3

Erklärung:
Die Funktion randrange aus dem random-Modul liefert eine Zahl zwischen 50 und 100 zurück (inklusive der unteren Schranke, exklusive der oberen). Will man als untere Schranke 0 haben genügt der Aufruf von randrange mit einem Parameter.

Durchlaufen einer Liste

# _liste.py # Durchlaufen einer Liste from turtle import * wertliste = [5, 12, 2, 4, 7, 12, 15, 17, 33, 24, 26, 32, 15, 6, 2] abstand = 10 pensize(3) pencolor("blue") lt(90) for wert in wertliste: # Durchlaufen der Liste fd(wert*5) bk(wert*5) pu() rt(90) fd(abstand) lt(90) pd() hideturtle()

Ergebnis:
16
Erklärung:
Statt wie bisher in jedem Schritt um einen Winkel weiterzudrehen, heben wir nun den Stift an und versetzen die Turtle jedesmal um die Strecke abstand nach rechts:

... pu() rt(90) fd(abstand) lt(90) pd() ...

Hier eine verbesserte Version mit farbigen Balken:

# _liste.py # verbessertes Programm. # Listenzugriff über Index. from turtle import * wertliste = [5, 12, 2, 4, 7, 12, 15, 17, 33, 24, 26, 32, 15, 6, 2] farbliste = ["red","violet", "cyan", "magenta", "green", "blue", "brown", "black", "beige", "orange", "purple","pink","yellow", "turquoise", "grey"] abstand = 10 pensize(3) pencolor("blue") i = 0 lt(90) for wert in wertliste: # Durchlaufen der Liste color(farbliste[i]) fd(wert*5) bk(wert*5) pu() rt(90) fd(abstand) lt(90) pd() i += 1 hideturtle()

Ergebnis:
16

Erklärung:
Diesmal holen wir den Farbwert wieder auf klassische Weise über den Listenindex.
Was passiert wenn farbliste weniger Elemente hat als wertliste?

Funktionen

# _funktion.py # Funktionen from turtle import * from random import * setup(450,450) pensize(5) speed(0) # 0:fastest 1(slowest)..10(fast) colors = ["red", "orange", "blue", "green"] def make_star(): for i in range(36): color(choice(colors)) len = randrange(20, 40) fd(len) bk(len) lt(10) for i in range(10): pu() goto(randrange(-150, 150), randrange(-150, 150)) pd() make_star()

Ergebnis:
17

Erklärung:
Funktionen sind ein gutes Mittel Programme sinnvoll zu strukturieren.
Eine Funktion wird mithilfe des def-Schlüsselworts definiert. Die Runden Klammern sind verpflichtend auch wenn keine Parameter übergeben werden. Wenn Python auf eine Funktionsdefinition stößt, wird diese nicht ausgeführt. Erst wenn die Funktion aufgerufen wird, wird sie abgearbeitet danach wird zum Punkt des Aufrufs zurückgesprungen.

Versuche "speed(0)" durch "trace(0,0)" zu ersetzen.
... setup(450,450) pensize(5) trace(0,0) colors = ["red", "orange", "blue", "green"] ...
Was beobachtest du?

Versuche dann "update()" am Ende der for-Schleife einzufügen:
... for i in range(10): pu() goto(randrange(-150, 150), randrange(-150, 150)) pd() make_star() update() # Diese Zeile einfügen ...
Was beobachtest du?

Hier noch ein tracer-Beispiel:
from turtle import * tracer(0,0) dist = 2 for i in range(200): fd(dist) rt(90) dist += 2

Ergebnis:
tracer

Noch ein weiteres Beispiel:

# _funktion.py # Funktionen, speed, Endlosschleife from turtle import * from random import * setup(600,600) def drawdot(): goto(randrange(-300,300), randrange(-300,300)) dot(randrange(10)**2,choice(["red", "green", "blue", "yellow"])) pu() hideturtle() speed(0) while True: drawdot()

Ergebnis:
17

Funktionen mit Übergabewert (Parameter)

# _funktionen_mit_parameter.py # Funktion mit Parameter from turtle import * from random import * setup(450,450) pensize(5) speed(0) colors = ["red", "orange", "blue", "green"] def make_star(x_pos, y_pos): pu() goto(x_pos, y_pos) pd() for i in range(18): color(choice(colors)) len = randrange(20, 40) fd(len) bk(len) lt(20) for x in range(5): for y in range(5): make_star(x*80-150, y*80-150)

Ergebnis:
18

Übung

Statt Punkten Polygone zeichnen mit zufälligen Ecken (Maximalzahl ist Parameter)

Funktionen mit Rückgabewert


# _funktionen_mit_parameter.py # Funktion mit Rückgabewert from turtle import * def is_prime(n): if n < 2: return False #for i in range(2,int(n**0.5)+1): # optimiert for i in range(2,n): if n%i == 0: return False return True width = 2 speed("fastest") tracer(0,1) setup(900,500) pensize(width) pu() goto(-400,-100) pd() lt(90) for i in range(400): if is_prime(i): color("blue") else: color("yellow") fd(200) bk(200) pu() rt(90) fd(width) lt(90) pd()

Ergebnis:
19

Erklärung:
Die Übernahme einer bereits geschriebenen Funktion (zum Beispiel is_prime) ist eine sehr häufige Form der Code-Wiederverwendung.
Mithilfe des return-Schlüsselworts wird die Funktion verlassen und der Wert an die aurufende Stelle übergeben. Funktionen die nur True oder False liefern, nennt man Boolsche-Funktionen.
Ansonsten arbeitet die Ausgabe das Programms ganz ähnlich wie das Programm, welches das Balkendiagramm zeichnet, mit dem Unterschied, dass der Abstand der Linien gleich der Linienbreite ist, eine Linie liegt also gleich unmittelbar neben der vorigen. Pensize (also die breite der einzelnen Linien) wurde in dieser Version mit 2 gewählt, weil das etwas besser zu sehen ist.
Anmerkungen zur Grafik:
Ereignisorientierte Programmierung, Rekursion siehe Kurs.


Ressourcen

Mehr Programme als zip-Datei zip icon

Videos

Hayley Denbraver - Recursion, Fractals, and the Python Turtle Module
EuroPython Conference 2018, Edinburgh
https://www.youtube.com/watch?v=b6AcYxIxXMA

Complete Python Turtle Graphics Overview! (From Beginner to Advanced)
https://www.youtube.com/watch?v=pxKu2pQ7ILo

Links

Stackoverflow, Fragen mit "Tag" [turtle-graphics]:
https://stackoverflow.com/questions/tagged/turtle-graphics?sort=votes&pageSize=15

Python Standard-Dokumentation zum Turtle-Modul:
https://docs.python.org/3/library/turtle.html

Sehr gehobene Python-Turtle Beispiele:
http://www.101computing.net/tag/python-turtle/

Koch-Schneeflocke als Online-Python Beispiel:
https://trinket.io/python/8c8b7f567e

Alternatives Turtle-Modul mit verbesserter Benutzerschnittstelle:
http://www.viktorianer.de/info/prog-frog.html

python4kids-Buch:
http://python4kids.net/turtle.html

Python-Turtle Spirograph:
http://www.101computing.net/python-turtle-spirograph/

Turtle → SVG


from canvasvg import canvasvg #use "pip install canvasvg" or "python -m pip install canvasvg" def saveImg(name): nameSav = name + ".svg" #ts = t.getscreen().getcanvas() ts = getscreen().getcanvas() canvasvg.saveall(nameSav, ts) hideturtle() #... saveImg("test")