Neues in Python 3.4

Viel Neues in der Standardbibliothek, einige Verbesserungen unter der Haube, keine syntaktischen Änderungen – so lässt sich das Ergebnis des 18 Monate dauernden Entwicklungszyklus für Python 3.4 zusammenfassen.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 29 Min.
Von
  • Christian Schramm
Inhaltsverzeichnis

Viel Neues in der Standardbibliothek, einige Verbesserungen unter der Haube, keine syntaktischen Änderungen – so lässt sich das Ergebnis des 18 Monate dauernden Entwicklungszyklus für Python 3.4 zusammenfassen.

Seit Mitte März steht die neue Version der Programmiersprache Python bereit. Die Highlights der Veröffentlichung sind die neuen Module asyncio, enum und pathlib, zudem liegt ein großer Schwerpunkt auf dem Thema Sicherheit.

So umfangreich Pythons Standardbibliothek ist, sie kann (und will) nicht alle Funktionen abdecken. Daher hat sich PyPi, analog zu Perls CPAN oder Rubys RubyGem, seit vielen Jahren als offizielle Sammelstelle für Python-Software etabliert. Der ursprüngliche Paketmanager easy_install wies allerdings einige Schwächen auf – am meisten vermissen seine Nutzer wohl die Fähigkeit, Pakete wieder zu deinstallieren. Da die Probleme von easy_install aufgrund von Design-Entscheidungen schlecht zu beheben waren, wurde es vor einiger Zeit vom neueren Paketmanager pip abgelöst.

Bislang war man allerdings gezwungen, pip von Hand herunterzuladen und zu installieren. Das war nicht nur etwas umständlich, sondern zudem ein potenzielles Sicherheitsrisiko. Da pip aber unabhängig von der Standardbibliothek entwickelt und in einem eigenen Release-Zyklus veröffentlicht wird, wäre eine direkte Übernahme in die Standardbibliothek nicht praktikabel. Daher wurde mit ensurepip ein Modul aufgenommen, mit dem sich pip in die aktuelle Python-Installation einfügen oder auf den neuesten Stand bringen lässt. Normalerweise muss der Endnutzer die Bibliothek allerdings nicht manuell benutzen – standardmäßig wird sie beim Kompilieren von Python gleich mit erstellt. Ebenso ist pip in einer virtuellen, mit pyenv erstellten Python-Umgebung sofort verfügbar.

Die Benutzung von pip ist denkbar einfach. Ein Paket wie Django lässt sich mit

$ pip install Django

installieren. Die Deinstallation ist mit

$ pip uninstall Django

ebenso selbsterklärend. Das Benutzerhandbuch zu pip beschreibt weitere Befehle und Optionen des Paketmanagers.

Das Gros der Änderungen ist in Pythons umfangreicher Standardbibliothek zu finden. Die Entwickler verfolgen das Ziel, dass sich typische Aufgaben in der Sprache ohne Fremdbibliotheken lösen lassen.

Um in die Standardbibliothek aufgenommen zu werden, muss ein Modul hohen Anforderungen genügen, denn es ist im Nachhinein nur noch schwer möglich, die API zu ändern. Da es andererseits oft schwierig ist, bevor ein Modul in der Standardbibliothek öffentlich getestet werden kann, vorauszusagen, was sinnvolle Schnittstellen sind, existiert seit Python 3.3 das Konzept der sogenannten Provisional API, also einer vorläufigen API.

Module, deren API mit provisional gekennzeichnet ist, geben noch keine Garantien bezüglich der Stabilität ihrer Schnittstellen – sie sollten folglich nur unter Vorbehalt in Produktions-Software zum Einsatz kommen. Im Allgemeinen lässt sich allerdings davon ausgehen, dass die meisten so gekennzeichneten Module ihre API nicht oder nur minimal verändern, bis sie – aller Voraussicht nach in der jeweils nächsten Version – als stabil deklariert werden. Im Folgenden sollen neue oder stark verbesserte Module der Standardbibliothek Thema sein.

Als Enum bezeichnet man in C/C++ und vielen anderen Sprachen eine Menge von (meist verwandten) Bezeichnungen, die sich durch Zuordnung eines konstanten Werts miteinander vergleichen lassen. Programmierer anderer Sprachen wird überraschen, dass Python über zwanzig Jahre lang nicht über ein Enum-Konstrukt verfügt hat. Erfahrene Python-Entwickler hingegen mögen gleichermaßen verblüfft darüber sein, dass es Menschen gibt, die dieses Feature in Python vermisst haben, da sich längst Konventionen herausgebildet haben, um ohne Enum auszukommen. Statt, wie in C, zu schreiben:

typedef enum {
DAY_MONDAY = 1,
DAY_TUESDAY = 2,
DAY_WEDNESDAY = 4,
/* ... */
} Day;

void handle_days(Day days) {
if (DAY_MONDAY & days) {
/* ...*/
}
}

void main() {
set_flags(DAY_MONDAY | DAY_WEDNESDAY);
}

nutzt man in Python typischerweise einfach Strings:

def handle_days(*days):
if "Monday" in days:
# ...

handle_days("Monday", "Wednesday")

Insbesondere Bindings zu C/C++-Bibliotheken nutzen eine andere Variante: Sie packen die Definitionen der Flags in ein separates Modul.

Inhalt day.py:

MONDAY = 1,
TUESDAY = 2,
WEDNESDAY = 4,

Inhalt main.py:

import day

def handle_days(days):
# ...

handle_days(day.MONDAY | day.WEDNESDAY)

Wieder andere Konventionen benutzen eine Klasse oder ein dict, um den Definitionen einen eigenen Namensraum zu geben.

Diese Möglichkeiten haben allerdings unterschiedliche Nachteile. Zwar sind Vergleiche von Strings in Python meist recht schnell, da oft nur ein Hash-Wert zu vergleichen ist – allerdings kann der Python- Parser Tippfehler in Strings nicht syntaktisch erkennen. Auch Syntax-Highlighter und Code-Vervollständigung in einer IDE sind für diese Variante nur eingeschränkt brauchbar. Zudem lassen sich die Strings nicht durch bitweise Operatoren (wie & und |) miteinander kombinieren, was die Kompatibilität mit C/C++-Bibliotheken erschwert.

Die Konstanten in einem anderen Modul unterzubringen (oder eine Klasse oder dict zu verwenden) vermeidet derartige Probleme. Dennoch haben diese Varianten eine subtilere, aber für die Python- Entwickler dennoch entscheidende Schwachstelle: Enum-Definitionen sind semantisch keine Zahlen. Ein Beispiel zeigt das besonders deutlich: Ein Wochentag ist keine ganze Zahl – dennoch tut man hier so, als ob. Operationen und Funktionen für Zahlen ergeben für Wochentage keinen rechten Sinn (was ist Montag plus Mittwoch?), und das gilt im Allgemeinen für die meisten Enums. Sie benötigen eine zugeordnete Zahl für die Identität, aber sie bedeuten nicht das, was der Zahlenwert darstellt.

Daher haben sich die Python-Entwickler entschlossen, eine spezielle Klasse namens Enum in der Standardbibliothek bereitzustellen, die derartige Probleme umgeht:

from enum import Enum

class Day(Enum):
Monday = 1
Tuesday = 2
Wednesday = 4
# ...

Sie verhalten sich nicht wie Zahlen, sondern unterstützen nur Vergleiche:

>>> Day.Monday is not Day.Wednesday
True
>>> Day.Monday is Day.Tuesday
False

Wenn Kompatibilität mit Integer-basierten Enums wie in C/C++ nötig ist, lässt sich hingegen IntEnum verwenden. Hier erben die Enum-Mitglieder von int:

class FileFlags(enum.IntEnum):
READ = 1
WRITE = 2
TEMP = 4

Auf Mitglieder von IntEnum lassen sich die bekannten bitweisen Operatoren anwenden:

>>> bool(FileFlags.READ & (FileFlags.WRITE | FileFlags.TEMP))
False