Python fΓΌr UmsteigerΒΆ

InΒ [Β ]:
print("Hallo Welt")

Über mich¢

  • Lehrer an der CWS Usingen (Mathe/Informatik/demnΓ€chst: Darstellendes Spiel)
  • Abgeordnet ans Schulportal (Moodle Theme)
  • Wir unterrichten an der CWS seit 2018 in GK mit Python

Python in der Sek IIΒΆ

python-erlass.png

Erfahrungen im Unterricht mit PythonΒΆ

  • Man redet weniger ΓΌber syntaktische Fehler und ist schneller bei semantischen Fehlern.

  • SchΓΌler haben mehr Erfolgserlebnisse

  • Python lΓ€sst sich fΓΌr verschiedene Themen verwenden (Objektorientierung, Web-Datenbank, KI, ...)

Neue Erfahrungen fΓΌr mich.ΒΆ

Python hat insbesondere in der Objektorientierung eine fundamental andere Herangehensweise als Java. Mein Blickwinkel auf Objektorientierung hat sich dadurch verΓ€ndert.

Das Python-Zen:

  * Simple is better than complex
  * flat is better than nested
  * Readability counts
  * If the implementation ist hard to explain, it's a bad idea
  * If the implementation is easy to explain, it may be a good idea.

Jack Diderich - Stop Writing Classes Jack Diderich - Stop Writing Classes(https://youtu.be/o9pEzgHorH0)

...oder: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

GrundlagenΒΆ

Auf einen Blick:

  • Es gibt keine Semikolons ;
  • Variablen werden ohne Typen deklariert. a = 3
  • Anstelle von Klammern {} bzw. BEGIN END starten BlΓΆcke mit einem : und werden eingerΓΌckt.
    for i in range(4):
       print(i)
    
  • Um AusdrΓΌcke werden keine Klammern gesetzt.
    if i > 3:
        i = i + 1
    
  • Es gibt keine Arrays
InΒ [Β ]:
import turtle
turtle.forward(100)
turtle.mainloop() # not needed in Thonny
InΒ [Β ]:
import turtle
ben = turtle.Turtle()
ben.forward(100)
turtle.mainloop()
InΒ [Β ]:
# Welche IDE?
  • Thonny ( https://thonny.org/ ) vollkommen ausreichend.
  • SchΓΌler nutzen oft selbst vscode oder pycharm

EinrΓΌckung und Schleifen:ΒΆ

InΒ [Β ]:
import turtle
for _ in range(4):
    for _ in range(4):
        turtle.forward(50)
        turtle.right(90)
    turtle.penup()
    turtle.forward(60)
    turtle.pendown()
    
turtle.mainloop() # not needed: in Thonny

VerzweigungenΒΆ

InΒ [Β ]:
from random import randint

for _ in range(1000):
    x = randint(-100, 100)
    y = randint(-100, 100)
    turtle.penup()
    turtle.speed(0)
    turtle.setpos(x, y)
    turtle.pendown()
    if  x < 0 == 0:
        turtle.color("red")
        turtle.dot(3)
    elif x < 50:
        turtle.color("grey")
        turtle.dot(3)
    else:
        turtle.color("blue")
        turtle.dot(3)

turtle.mainloop() # not needed: in Thonny

TextadventuresΒΆ

InΒ [Β ]:
end = False
state = "start"
while not end:
    if state == "start":
        text = "Du stehst vor der Sporthochschule - Wo gehst du hin?"
        choice = input(text)
        if choice == "t":
            state = "tagungsraum"
        elif choice == "q":
            end = True
    elif state == "tagungsraum":
        text = "Du bist im Tagungsraum"
        choice = input(text)
    

DatentypenΒΆ

Python ist dynamisch typisiert:

InΒ [1]:
x = 3
print(x)
print(type(x))
x = 4.0
print(type(x))
x = "4"
print(type(x))
3
<class 'int'>
<class 'float'>
<class 'str'>
InΒ [3]:
x = input("Gib die erste Variable an")
y = int(input("Gib die zweite Variable an"))
print(x + y)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 3
      1 x = input("Gib die erste Variable an")
      2 y = int(input("Gib die zweite Variable an"))
----> 3 print(x + y)

TypeError: can only concatenate str (not "int") to str

Datentypen und OperatorenΒΆ

System.out.println("Ausgabe: " + 1);

---> "Ausgabe:1"

Pascal/Delphi

writeln('Ausgabe: ' + 1);

---> Error

Python verhΓ€lt sich wie Pascal/Delphi:

print("1" + 1)

Numerische DatentypenΒΆ

  • Es gibt nur int, float und complex
  • Es gibt keine maxint-Eigenschaft
InΒ [4]:
lightyear_to_meter: int = 9460730472580800
milky_way_diameter = 100_000 * lightyear_to_meter

print(f"The milky way has a diameter of approximately {milky_way_diameter} meters")
The milky way has a diameter of approximately 946073047258080000000 meters
InΒ [Β ]:
Aber float-Werte kΓΆnnen "ΓΌberlaufen" - Es wird dann allerdings ein Fehler geworfen.
InΒ [5]:
a = 2.1
while a > 0:
    a = a ** 2
print(a)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
Cell In[5], line 3
      1 a = 2.1
      2 while a > 0:
----> 3     a = a ** 2
      4 print(a)

OverflowError: (34, 'Numerical result out of range')

Boolean DatentypenΒΆ

Man vergleicht booleans mit and, or und not Beispiel:

if not a and not b:
    do_something()

ListenΒΆ

  • Python nutzt standardmÀßig Listen im Gegensatz zu Delphi/Java (arrays)
  • Listen kΓΆnnen unterschiedliche Datentypen enthalten.
  • Listen kΓΆnnen dynamisch verlΓ€ngert werden.
InΒ [Β ]:
a = [3, "Hallo Welt"]
for value in a:
    print(value)
InΒ [Β ]:
a.append("Noch ein Wert")
print(a)

Listen - Spezielle Operationen:ΒΆ

InΒ [Β ]:
a = [3, "Hallo Welt"]

if (3 in a):
    print("Ok")
InΒ [Β ]:
a = [3, 4, 2]

if a:
    print("Die Liste ist nicht leer")

LaufzeitenΒΆ

In der Python-Dokumentation kann man die Laufzeiten fΓΌr Listen-Operationen nachschlagen: runtimes.png

ArraysΒΆ

Theoretisch gibt es auch Arrays:

InΒ [Β ]:
import array
a = array.array('i', [10, 20, 30])
print(a)
print(a[1])

List ComprehensionsΒΆ

InΒ [Β ]:
## List Comprehensions
lst = [x for x in range(0, 10) if x  % 2 == 0]
print(lst)

Über Listen iterieren¢

Die Java-Variante for (i=0;i<a.length;i++) ist in Python nicht vorhanden, stattdessen:

InΒ [Β ]:
lst = range(0, 10)
for value in lst:
    print(value)
InΒ [Β ]:
lst = range(0, 10)
for index, value in enumerate(lst):
    print(index, value * value)

DictionariesΒΆ

InΒ [Β ]:
telefonbuch = {"Stefan": 3213, "Klaus": 3212, "Stefan": 3021}
InΒ [Β ]:
print(telefonbuch["Stefan"])

FunktionenΒΆ

InΒ [Β ]:
def add(a, b, c = 0):
   return a + b + c

print(add(3, 4))
InΒ [Β ]:
  * Funktionen kΓΆnnen Default-Parameter enthalten.

Funktionen als "First Class-Objects"ΒΆ

InΒ [Β ]:
def add(a, b, c = 0):
   return a + b + c

def action(func, a, b, c = 0):
    return func(a, b, c)


print(action(add, 3, 4))
  • Funktionen sind selbst auch Objekte und kΓΆnnen als solche ΓΌbergeben werden.
InΒ [Β ]:
print(type(add))
InΒ [Β ]:
a = add
a(3, 4)

ObjektorientierungΒΆ

Ein einfaches Beispiel fΓΌr eine Klasse.

InΒ [Β ]:
class BankAccount:
    def __init__(self):
        self.balance = 0

andreasKonto = BankAccount()
print(andreasKonto.value)
  • __init__ wird aufgerufen, nachdem die die Instanz instanziiert wird.

Achtung: Typischer Fehler von Java-Umsteigern:ΒΆ

InΒ [Β ]:
class BankAccount:

    balance = 0
    
    def __init__(self):
        pass

andreasKonto = BankAccount()
martinKonto = BankAccount()
print(id(andreasKonto.balance), id(martinKonto.balance))
  • balance ist hier eine Klassenvariable

Methoden:ΒΆ

InΒ [Β ]:
class BankAccount:
    def __init__(self):
        self.balance = 0
        self.interest_rate = 0.03

    def deposit(self, balance):
        self.balance += balance

    def withdraw(self, balance):
        self.balance -= balance

    def add_interest(self):
        self.balance += self.balance * self.interest_rate
    

        
andreasKonto = BankAccount()
andreasKonto.deposit(20)
andreasKonto.add_interest()
print(andreasKonto.balance)
  • Klassen werden im CamelCase geschrieben, Methoden mit snake_case.
  • Es fehlt noch Kapselung...

Kapselung - Teil 1ΒΆ

Python: Kapselung funktioniert anders als in Java und Delphi:

Mit Properties kann der Zugriff auf Attribute eingeschrΓ€nkt werden - Der Rest der Klasse muss dafΓΌr nicht verΓ€ndert werden!

InΒ [14]:
class BankAccount:
    def __init__(self, balance):
        self._balance = balance
        self.interest_rate = 0.03

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, value):
        self._balance = value

    def deposit(self, balance):
        self.balance += balance

    def withdraw(self, balance):
        self.balance -= balance

    def add_interest(self):
        self.balance += self.balance * self.interest_rate
    

        
andreasKonto = BankAccount(20)
andreasKonto.deposit(20)
andreasKonto.add_interest()
print(andreasKonto.balance)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[14], line 22
     17         self.balance += self.balance * self.interest_rate
     21 andreasKonto = BankAccount(20)
---> 22 andreasKonto.deposit(20)
     23 andreasKonto.add_interest()
     24 print(andreasKonto.balance)

Cell In[14], line 11, in BankAccount.deposit(self, balance)
     10 def deposit(self, balance):
---> 11     self.balance += balance

AttributeError: property 'balance' of 'BankAccount' object has no setter

Vergleich mit Java/Delphi:ΒΆ

Python verfolgt ein anderes Paradigma beim Entwurf des Systems:

wasserfall-agile.png

Idee Java/Delphi:

  • Sichtbarkeiten werden im Voraus definiert
  • Sichtbarkeiten sind Regeln, die auch von Programmiererinnen eingehalten werden mΓΌssen.

Idee Python:

  • Es kann ein Prototyp erstellt werden, der ohne Sichtbarkeiten auskommt.
  • Sichtbarkeiten kΓΆnnen im Nachhinein hinzugefΓΌgt werden.
  • Sichtbarkeit sind Konventionen(!), auf die man sich geeinigt hat.
  • Entwickler sind "erwachsene Leute".

_ und __ΒΆ

Technisch sind dies die Entsprechungen fΓΌr public/protected/private in Java:

self.attribute # public
self._attribute # protected
self.__attribute # private / name_mangling

Aber: In Python gilt nicht die Regel, dass alle Attribute private sein sollten. PEP8 sagt dazu:

"Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers."

Meine HerangehensweiseΒΆ

Im Grundkurs hat sich folgende Herangehensweise bewΓ€hrt:

  • self._attribute wird fΓΌr private Attribute verwendet,
  • self.attribute fΓΌr ΓΆffentliche Attribute.
  • Name Mangling habe ich bisher ausgeblendet.
  • Properties habe ich exemplarisch verwendet, ansonsten einen eher Java-artigen Zugang zu Kapselung.

Vererbung InterfacesΒΆ

Es gibt keine Entsprechung fΓΌr (Java) Interfaces in Python.

Aber Mehrfach-Vererbung:

InΒ [Β ]:
class Shape():
    pass

class Printable():
    def print(self):
        pass

class Rectangle(Shape, Printable):
    def __init__(self, x, y, width, height):
        self.x, self.y, self.width, self.height = x, y, width, height
    def print(self):
        return(self.x, self.y, self.width, self.height)

r = Rectangle(3, 5, 4, 2)
print(r.print())
InΒ [Β ]:
from abc import ABC, abstractmethod

class Shape(ABC):
    pass

class Printable(ABC):
    @abstractmethod
    def print(self):
        pass

class Rectangle(Shape, Printable):
    def __init__(self, x, y, width, height):
        self.x, self.y, self.width, self.height = x, y, width, height
    def print(self):
        return(self.x, self.y, self.width, self.height)

r = Rectangle(3, 5, 4, 2)
print(r.print())

Type HintsΒΆ

InΒ [Β ]:
from typing import Type

class Client:
    def __init__(self):
        self.partner = None

class SimpleNetwork:
    def __init__(self):
        self.client1 : Type[Client] = Client()
        self.client2 : Type[Client] = Client()

    def get_client1(self) -> Type[Client]:
        return self.client1

network =  SimpleNetwork()
print(type(network.client1))
print(network.get_client1())

Mein Zugang zu ObjektorientierungΒΆ

Miniworldmaker - Objects firstΒΆ

So sieht Code in der E-Phase aus:

InΒ [15]:
import miniworldmaker

board = miniworldmaker.Board()
r = miniworldmaker.Token((20, 20))
__
@r.register
def act(self):
    self.x += 1
    
@r.register
def on_key_pressed(self, keys):
    if 's' in keys:
        self.y = self.y +1

board.run()
pygame 2.5.2 (SDL 2.28.2, Python 3.11.5)
Hello from the pygame community. https://www.pygame.org/contribute.html
init miniworldmaker app...
Show new miniworldmaker v.2.18.3.0 Window
Let's go
can't check if run() is present (This can happen if you are using jupyter notebooks. Resuming)
An exception has occurred, use %tb to see the full traceback.

SystemExit: 0
/home/andreas/code/python/jupyter/fobi-python/env/lib/python3.11/site-packages/IPython/core/interactiveshell.py:3534: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

...und so in der Q-Phase:

InΒ [Β ]:
import miniworldmaker
import random

board = miniworldmaker.Board()

class MyToken(miniworldmaker.Token):

    def on_setup(self):
        self.x = random.randint(0,400)
        self.y = random.randint(0, 400)
        
    def act(self):
        self.x+= 1

    def on_key_pressed(self, keys):
        self.y = self.y + 1

for _ in range(10):
    MyToken()
    
board.run()

GUIΒΆ

Es gibt in Python viele GUIS: https://wiki.python.org/moin/GuiProgramming

  • TKinter - Weit verbreitet
  • guizero, pysimplegui, (appjar) - Gut geeignet im Bildungskontext.

GuiZeroΒΆ

InΒ [Β ]:
from guizero import App, Text, PushButton, TextBox

def change_message():
    label.value = f"Hello {input_box.value}"

app = App(height= 130, width = 200, title="Hello world")
input_box = TextBox(app, width = 200)
button = PushButton(app, text="Press me", command=change_message)
label = Text(app, text="")
app.display()

PySimpleGuiΒΆ

InΒ [Β ]:
import PySimpleGUI as sg

layout = [[sg.Text("What's your name?")],
          [sg.Input(key='-INPUT-')], # Define key '-INPUT'
          [sg.Text(size=(40,1), key='-OUTPUT-')], # Define key '-OUTPUT'
          [sg.Button('Ok'), sg.Button('Quit')]]

window = sg.Window('Window Title', layout)

while True:.
    event, values = window.read()
    if event == sg.WINDOW_CLOSED or event == 'Quit':
        break
    window['-OUTPUT-'].update(f"Hello {values['-INPUT-']}!")

window.close()

tkinterΒΆ

InΒ [Β ]:
import tkinter as tk

window = tk.Tk()

def update():
    global tk_label, tk_input
    tk_label["text"] = f"Hallo {tk_input.get()}"
    
tk_input = tk.Entry(window)
tk_input.pack()  
tk_label = tk.Label(window, text="")
tk_label.pack()  
tk_button = tk.Button(window, text = "Absenden", command=update)
tk_button.pack()
    
tk.mainloop()

Unser VorgehenΒΆ

InΒ [Β ]:
  * Sehr projektorientiert
  * Begleitende Moodle-Kurse mit Code-Runner und neuerdings H5P

Bildschirmfoto vom 2023-09-25 06-19-41.png

Bildschirmfoto vom 2023-09-25 06-20-11.png

Bildschirmfoto vom 2023-09-25 06-21-07.png

Siehe auch...ΒΆ

  • https://blu3r4y.github.io/python-for-java-developers/?print-pdf - Sehr umfangreife Beschreibung, wie sich Python und Java unterscheiden.
  • https://cscircles.cemc.uwaterloo.ca/de/- Programmierkurs der University of Waterloo