Python 4.1 – Was Du noch nicht kennst

Herzlich Willkommen im letzten Modul "Was du noch nicht kennst".

In dieser Lektion wollen wir ein paar erweiterte Konzepte vorstellen, die wir bisher noch nicht besprochen haben.

Es ist an dieser Stelle nicht wichtig, dass du diese Konzepte in ihrer Ganzheit verstehst - das ist in dieser Kürze auch nicht möglich.

Dieses Phänomen wird dir in deiner Programmierlaufbahn immer wieder begegnen. Oft ist es zunächst für das Verständnis ausreichend, grob zu wissen worum es geht. Erweiterte Kenntnisse kannst du dir dann an entsprechender Stelle aneignen.

Wir wollen im Verlauf dieser Lektion folgende Konzepte ansprechen:

  1. Objektorientierte Programmierung
  2. Lambdas
  3. Threads

1. Objektorientierte Programmierung

Objektorientierte Programmierung ist eine Art der Programmierung, die sich mit der Modellierung von realen Verhältnissen beschäftigt. Kurz gesagt gibt es bei der objektorientierten Programmierung Klassen, die Schablonen für Objekte darstellen. Wir wollen uns das Ganze anhand eines Beispiels verdeutlichen. Nehmen wir an, wir wollen ein Ritterregiment modellieren. Jeder Ritter hat einen Namen und eine Größe. Zudem hat jeder Ritter die Möglichkeit, einen Feind anzugreifen. Eine Modellierung dieses Sachverhalts könnte so aussehen:

class Ritter:

def __init__(name, groesse):
self.name = name
self.groesse = groesse

def angreifen(self, feind):
    print(self.name + " greift " + feind " an.")

Wie bereits erwähnt, stellt die Klasse die Schablone für alle Instanzen (auch Objekte genannt) der Klasse Ritter da. Wir definieren in dieser Schablone die beiden Attribute Name und Groesse, die jede Instanz dieser Klasse haben wird. Ebenso definieren wir die Methode angreifen.

Dadurch wird festgelegt, dass jede Instanz dieser Klasse die beiden Attribute Name und Groesse hat, und angreifen kann. Um eine neue Instanz der Klasse Ritter zu erstellen, können wir folgenden Code benutzen.

Code-Beispiel - Neue Objektinstanz erstellen:

lancelot = Ritter(name="Lancelot", groesse=180)

Wir erhalten dadurch ein Objekt mit der Identität lancelot vom Typ Ritter. Wir können nun auf die Attribute des Objekts zugreifen:

print(lancelot.name)
print(lancelot.groesse)

Zudem können wir unseren Ritter mit folgendem Code angreifen lassen:

lancelot.angreifen("Drache")

Die Objektorienterte Programmierung kennt noch einige weitere Konzepte wie Vererbung und Polymorphie, die an dieser Stelle nur erwähnt sein sollen. Im Allgemeinen kann man mit Objektorienter Programmierung Sachverhalte logisch in Klassen kapseln, was der Übersichtlichkeit zuträglich ist.

Zudem erlaubt sie eine modulare und damit gut erweiterbare und wieder verwendbare Codestruktur. Python basiert im Wesentlichen auf Objektorientierte Programmierung und komplexere Programme sollten objektorientiert programmiert werden.

2. Lambdas

Lambdas gehören in die Welt der funktionalen Programmierung. In der funktionalen Programmierung werden Computerprogramme als Funktionen verstanden, die für eine Eingabe eine definierte Ausgabe liefern, wobei die Ausgabe ausschließlich von der Eingabe abhängt. Python ist keine funktionale Programmiersprache im eigentlichen Sinn, erlaubt aber die Benutzung von Lambdas für anonyme Funktionen. Eine anonyme Funktion kann als eine Art Kurzschreibweise für eine, oftmals nur einmalig benötigte Funktion, angesehen werden. Wir wollen uns das ganze anhand eines Beispiels verdeutlichen

Code-Beispiel - Lambdas:

square = lambda a : a * a
print(square(20))
Code ausprobieren ×

In diesem Fall definieren wir die Lambda-Funktion square. Diese Funktion akzeptiert den Paramater a und multipliziert a mit a. Im Allgemeinen folgt die Syntax der Lambda-Funktion folgendem Schema:

lambda Argumente: Expression

Diese Funktion können wir dann mit dem Parameter 20 aufrufen. Auf den ersten Blick sieht das nicht besonders aufregend aus. Besonders gern werden aber z.B. lambda-Funktionen mit der Funktion filter benutzt.

Der Filter wird dazu benutzt, eine Liste anhand bestimmter Kriterien zu filtern. Klingt logisch, oder?

Dabei akzeptiert filter eine Funktion, die diese Kriterien definiert. Wir wollen uns dazu ein Beispiel ansehen. Wir nehmen dazu eine Liste, aus der alle geraden Zahlen extrahieren und in eine neue Liste speichern wollen. Ohne Lambdas könnte der Code dazu etwa so aussehen:

Code-Beispiel - Filter:

liste_ursprung = [1, 2, 3, 4, 5, 6, 7, 8, 9]
liste_final = list()

for number in list:
    if (number % 2==0):
    liste_final.append(number)
Code ausprobieren ×

Wir können das ganze etwas abkürzen, wenn wir für die for-Schleife eine Kurzschreibweise benutzen.

Beispiel 3:

liste_ursprung = [1, 2, 3, 4, 5, 6, 7, 8, 9]
liste_final = [number for number in liste_ursprung if number%2==0]

Dieser Code ist bereits wesentlich kürzer und entspricht semantisch genau dem ersten Beispiel. Wir können dafür aber auch Lambdafunktionen benutzen:

Code-Beispiel - Filter mit Lambdas (die elegante Lösung):

liste_ursprung = [1, 2, 3, 4, 5, 6, 7, 8, 9]
liste_final = list(filter(lambda x: (x%2 == 0), liste_ursprung)

Hier verwenden wir die Lambda-Funktion als anonyme Funktion. Semantisch entspricht diese Funktion den beiden vorigen Code-Beispielen.

Threads

Bisher liefen alle deine Programme synchron ab. Das bedeutet, dass jedes Programm einen Befehl nach dem anderen abgearbeitet hat. Jeder Befehl wurde erst begonnen, nachdem der vorherige Befehl vollständig abgeschlossen war.

Für eine ganze Reihe von Programmen ist dies aber sehr ungünstig. Denke z.B. an ein Programm, das eine Datei aus dem Internet lädt. Es gibt meist keinen Grund, warum dein Programm vollständig auf einen Download warten soll.

Als Lösung für dieses Problem bieten die meisten Programmiersprache, Pyhton inklusive, das Konzept der Threads an. Ein Thread (englisch für Faden) ist eine separate Ausführungseinheit. Wir wollen uns auch hierzu ein Beispiel ansehen.

Code-Beispiel - Threads:

import time
import threading

def myfunc(i):
    print("sleeping 5 sec from thread %d" % i)
    time.sleep(5)
    print("finished sleeping from thread %d" % i)

for i in range(10):
    t = threading.Thread(target=myfunc, args=(i,))
    t.start()
Code ausprobieren ×

Hier erzeugen wir 10 Threads, die gleichzeitig laufen. Dann warten wir auf die Abarbeitung der Funktion, deren Hauptteil einfach darin besteht, fünf Sekunden zu schlafen.

Zusammenfassung:

Wie du gesehen hast, gibt es in Python noch eine Menge weitere Konzepte, die dir die Arbeit erleichtern und viele Dinge erst möglich machen.

Besonders mit objektorientierter Programmierung wirst du dich auf deiner Reise in das Land der Pythons ausgiebig beschäftigen. Wichtig dabei ist weniger, auf Anhieb alles zu verstehen, sondern mehr, zu Wissen welche Möglichkeiten Python bietet.

Wir hoffen, du hattest Spaß und wir sehen uns in der nächsten Lektion wieder.