Blog

ML Summit
Das große Trainingsevent für Machine Learning Development & Business Innovation
26
Aug

Python: „Wir sehen den Trend, immer mehr Support für Typisierung einzuführen“

Oz Tiram

Die Programmiersprache Python erfreut sich wachsender Beliebtheit. Vor allem im Bereich des maschinellen Lernens ist Python gesetzt. Wir haben uns mit Oz Tiram, Senior IT-Architekt bei der noris network AG und Sprecher auf dem Python Summit 2019, über die Gründe für Pythons Popularität sowie den aktuellen Stand der Python-Sprachentwicklung unterhalten.

Asynchrone Programmierung & Koroutinen in Python

Python Summit: In deinem Workshop auf dem Python Summit behandelst du Python’s Iterables, Iterators und Generators. Kannst du kurz erläutern, was das Besondere dabei ist?

Oz Tiram: Nun, eigentlich nichts! Fast alle Programmiersprachen verfügen über ähnliche Konstrukte und wenn nicht, dann ist es einfach, sie zu implementieren. Das Coole an Python ist aber, dass die Sprache schon so viel von Haus aus mitbringt. Um damit produktiv zu arbeiten, muss man solche Dinge wie Iterables, Iterators und Generators nicht selbst implementieren.

Das Coole an Python ist, dass die Sprache schon so viel von Haus aus mitbringt.

Jedes beliebige Python-Objekt, das andere Objekte beinhaltet, ist iterierbar. Natürlich ist das bei anderen Sprachen auch so – also keine Überraschung hier. Computer-Programme sind nun einmal dazu da, um repetitive Aufgaben für uns zu erledigen. Deshalb braucht jede Sprache einen Mechanismus, um eine Aktion zu wiederholen, bis sie nicht mehr gebraucht wird. Eine Art, das zu realisieren, ist das Wiederholen einer Aktion auf eine Menge von Elementen. In menschlicher Sprache formuliert wäre das:

Für jedes Element aus einer Menge an Elementen: Tue etwas mit dem Element.

In Python wird dieser Satz so ausgedrückt:

>>> for item in BagOfItems:
...   do_something(item)

In diesem Fall ist BagOfItems ein Iterable. In meinem Workshop auf dem Python Summit schauen wir uns zum Beispiel an, wie so eine BagOfItems gebaut wird und wie Python weiß, wann es aufhören soll, wenn BagOfItems keine Elemente mehr enthält.

Iteratoren sind eine spezielle Art von BagOfItems, bei der wir auf jedes Item einzeln zugreifen können. Normalerweise wollen wir, wenn wir mit einer Aufgabe begonnen haben, diese auch fertig stellen. Nehmen wir zum Beispiel statt BagOfItems einmal BagOfBalls – also einen Sack voller Bälle. Wenn ein Mensch nun alle roten Bälle in dem Sack finden möchte, dann könnte er einen Ball aus dem Sack nehmen und prüfen, ob dieser rot ist, und gegebenenfalls in den Korb für rote Bälle werfen. Bälle einer anderen Farbe kämen entsprechend in andere Körbe. Ein Mensch kann hier einfach einmal eine Kaffeepause einlegen und dann weiter machen. Der Sack ist der Iterator, weil wir ein Element nach dem anderen aus ihm nehmen können. Ein Iterable erlaubt es hingegen nur, alle Bälle auf einmal ohne Pause zu prüfen. Wenn es also darum geht, bestimmte Aufgaben mehrfach auszuführen, brauchen wir in der Praxis manchmal eine Pause.

Ein Generator ist, einfach gesagt, eine bestimmte Art von Funktion, die uns dabei hilft, iterierbare Elemente in einen Iterator zu verwandeln. Ein Generator kann auch Iteratoren erzeugen, ohne auf lokal gespeicherte Daten zurückzugreifen. Aus diesem Grund können Generatoren auch große Datenmenge bearbeiten, ohne diese im Speicher halten zu müssen. Weil Generator-Funktionen so nützlich sind, haben sie in Python ein eigenes Schlüsselwort: yield. Generatoren sind Funktionen, die kein return-Statement aufweisen. Stattdessen können sie eine oder mehrere yield-Anweisungen in ihrem Body-Bereich haben:

   >>> def odd_counter(max):
   ...     x = 0
   ...     while x <= max:
   ...         if not x%2 == 0:
   ...             yield x
   ...         x += 1
   ...
   >>> odd_counter(10)
   <generator object odd_counter at 0x7f771f800258>

Beim Aufruf eines Generators wird eine Generator-Instanz erzeugt – der Generator wir also nicht gleich gestartet. Um den Generator dann tatsächlich zu starten, wird das nächste Element im Generator aufgerufen:

>>> c = odd_counter(10)
>>> next(c)
1
>>> next(c)
3
...
>>> next(c)
 Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
 StopIteration

Das führen wird weiter so durch, bis der Code abgearbeitet ist. An dieser Stelle wirft der Generator dann eine StopIteration Exception.

Python Summit: Kannst du einmal ein Beispiel geben, wie mit den beschriebenen Sprachfeatures asynchrone Programmierung realisiert werden kann?

Oz Tiram: Da Generatoren nicht unmittelbar gestartet werden, können wir etliche davon erzeugen und eine Methode dazu schreiben, um Generatoren bei Bedarf aufzurufen – eine Art Scheduler. Wenn eine Koroutine die Ressourcen des Betriebssystems nutzen kann, blockiert dies andere Koroutinen. Es gibt in Python allerdings Mechanismen, um zwischen blockierenden Koroutinen hin und her zu wechseln, wenn eine blockierende Anweisung ausgeführt werden muss. Das ist übrigens schon ein altes Feature in Python, das seit Python 2.5 existiert, aber erst so richtig genutzt wurde, als in späteren Versionen die Schlüsselworte async und await eingeführt wurden. Um darüber mehr zu erfahren, empfehle ich, sich den entsprechenden PEP 342 anzuschauen, insbesondere die Implementierung eines solchen Scheduler.

Python Summit: Wie funktionieren die Koroutinen in Python?

Oz Tiram: Koroutinen in Python sind etwas eigenwillig. Wie Generatoren führen sie auch das Schlüsselwort yield in ihrem Body-Bereich. Allerdings erscheint yield in einer Koroutine direkt rechts vom Assignment-Operator. Koroutinen sind der nächste Schritt in der Entwicklung von Generatoren. Jedes Mal, wenn die Ausführung eines Generator-Codes auf einen yield-Ausdruck stößt, wird der Wert rechts von yield an den Aufrufer gesendet. Gleichzeitig wird aber auch die Ausführung des Generators bis zum next-Aufruf gestoppt. Der Aufrufer kann in der Zwischenzeit andere Aufgaben übernehmen.

Eine Koroutine kann nicht nur Werte produzieren, sondern auch Werte vom Aufrufer erhalten. Dafür wird der Ausdruck .send(DATA) genutzt, um Koroutinen mit Werten zu versorgen. Der Aufrufer kann durch .close() die Ausführung anhalten oder via .throw sogar Exceptions in Koroutinen werfen lassen.

Hier ist ein einfaches Beispiel für die Definition und Nutzung einer Koroutine:

>>> def hello_coro(subject):
...     print("Searching for: %s" % subject)
...     while True:
...         message = (yield)
...         if subject in message:
...             print("Found %s in %s" % (subject, message))
...

>>> # calling the coroutine does not run it
>>> coro = hello_coro("Python Summit")
>>> # advance the coroutine to the next yield statement (priming)
>>> next(coro)
Searching for: Python Summit
>>> coro.send("This will do nothing")
>>> coro.send("The Python Summit 2019 will be awesome")
Found Python Summit in The Python Summit 2019 will be awesome

Python und Machine Learning

Python Summit: Python ist im Machine-Learning-Umfeld sehr beliebt. Weshalb ist das so? Gibt es in der Sprache hier besonders viele semantische Hilfe? Liegt es an den verfügbaren Bibliotheken? Oder sind es andere Gründe?

Python verfolgt den Ansatz, für die meisten Anwendungsfälle „schnell genug“ zu sein.

Oz Tiram: Einer der Gründe ist sicherlich, dass Python sehr populär ist. Auf Stackoverflow liegt Python aktuell ganz vorne, und in Rankings wie dem RedMonk-Popularitätsindex oder Tiobe ist die Sprache immer unter den Top 3 – allenfalls überholt von Java und JavaScript.

Generell ist Python nicht unbedingt die schnellste Sprache – dabei beziehe ich mich auf die Referenzimplementierung cPython, die die am weitesten verbreitete Version ist. Das liegt daran, dass Python nunmal eine interpretierte Sprache und keine kompilierte Sprache ist. Aber Python verfolgt den Ansatz, für die meisten Anwendungsfälle „schnell genug“ zu sein, und stellt Entwickler-Geschwindigkeit über Ausführungsgeschwindigkeit. Wenn Python-Code wirklich einmal schnell ausgeführt werden muss, kann er über die C-Programmiersprache optimiert werden. Python selbst ist ja in C geschrieben, und es ist nicht schwierig, Python mit C-basierten Modulen zu erweitern.

Im Bereich des maschinellen Lernens und der Data Science wird viel Quellcode für Prototypen geschrieben, um schnell einmal etwas auszuprobieren. In den meisten Fällen ist es hier nicht wichtig, dass der Code gut performt. Performance kommt dann ins Spiel, wenn es darum geht, große partielle Differentialgleichungen zu lösen oder schwierige CPU-basierte algebraische Kalkulationen durchzuführen.

Deshalb wird der meiste Machine Learning Code nicht direkt in Python geschrieben, sondern eher als Extensions in C. Numpy, SciPy, Pandas und TensorFlow sind solche C-basierten Libraries, die direkt von Python aus aufgerufen werden können. Man erhält so den Komfort von Python zur Geschwindigkeit von C.

Was Python unter Wissenschaftlern – und deshalb auch im Bereich des maschinellen Lernens – so populär machte, ist der Fakt, dass die Sprache einfach zu lesen und zu schreiben ist. Seit einigen Jahren ist Python in den USA zudem die verbreitetste Sprache in Anfängerkursen zur Programmierung. Es ist deshalb keine Überraschung, dass die Popularität Pythons zunimmt.

Python verfügt an sich über keine semantischen Hilfskonstrukte für Machine Learning. Aber die Popularität der Sprache sowie ihre einfache Nutzbarkeit macht sie eben (auch) interessant für den Bereich KI und Machine Learning. Eine Anekdote über Data Science mit Python lautet, dass die Popularität der Bibliothek Numpy, die für Matrix-Kalkulationen und verschiedene algebraische Operationen genutzt werden kann, die Python-Sprachentwickler dazu gebracht hat, in Python 3.5 einen neuen Operator einzuführen: den Matrix Multiplikationsoperator @. In Python selbst wird der Operator sonst von keinem eingebauten Konstrukt verwendet, aber Numpy macht davon starken Gebrauch.

Python – auf dem Weg zur Typisierung

Python Summit: Wo steht die Python-Sprachentwicklung gerade? Welche Neuerungen sind im Gespräch?

Oz Tiram: Python entwickelt sich sicherlich weiter, doch auch der Spirit, dass die Sprache einfach erlernbar bleiben sollte, ist weiterhin präsent. Mit Python 3 sahen wir einen Trend, in die dynamische Sprache immer mehr Support für Typisierung einzuführen. Python 3 führt optionale Funktionsannotationen wie diese ein:

>>> def foo(a: int, b: float=5.0) -> int:
...     pass
...

Weiter ging es in Python 3.6 mit Type-Annotationen für Variablen:

   primes: List[int] = []

   captain: str  # Note: no initial value!

   class Starship:
        stats: Dict[str, int] = {}

Python 3.8 wird dann typisierte Dictionaries bringen:

 from typing import TypedDict

   class Movie(TypedDict):
       name: str
       year: int

All das bedeutet, dass Python so langsam aussieht wie Java oder eine andere typisierte Sprache. Aber es sieht nur so aus, denn alle Typisierung in Python ist optional. Das heißt, dass man für kleine Skripts zur einmaligen Nutzung alles so belassen kann wie bisher, man für größere Projekte, bei denen Typisierung gewünscht ist, aber auch Typen hinzufügen kann. Ich schätze, die Popularität Pythons wird dadurch eher noch steigen und das Bild von Python als „nur eine Skriptsprache“ wird immer mehr verblassen. Ein interessantes Projekt, das auf diesen Neuerungen aufbaut, ist das Tool zur statischen Analyse MyPy. Hier ist ein Zitat aus der Dokumentation:

„MyPy ist ein statischer Type Checker für Python 3 und Python 2.7. Wenn dein Code Typisierungen enthält, kann MyPy die Typisierung überprüfen und verbreitete Bugs aufspüren.“

Ich glaube außerdem, dass es ein wachsendes Ökosystem von Compilern geben wird, um Python zu nativem Code zu kompilieren. Der bekannteste ist Cython, mit dem es möglich ist, Python-Code zu schreiben und dann über eine Übersetzung nach C in Maschinensprache zu kompilieren. Ein ähnliches Projekt, das in jüngerer Vergangenheit ganz neu aufgesetzt wurde, ist nuitka, und ich glaube, andere werden folgen.

Python Summit: Vielen Dank für dieses Interview!

Geschrieben von: Hartmut Schlosser

Content-Stratege, IT-Redakteur, Storyteller – als Online-Teamlead bei S&S Media ist Hartmut Schlosser immer auf der Suche nach der Geschichte hinter der News. #java #eclipse #devops #machinelearning #seo. Zum Lächeln bringen ihn kreative Aktionen, die den Leser bewegen. @hschlosser