(c) Galyna Andrushko, 123rf.com
Funktionale Programmierung (2): Python funktional
Auf neuen Wegen
von Rainer Grimm
15.10.2009
Die meisten Entwickler verbinden Python mit dem objektorientierten Programmierstil. Doch die Sprache beschreitet vermehrt auch die Pfade der funktionalen Programmierung. Dieser Artikel zeigt, was Python in Sachen Closures, List Comprehension und Funktionen höherer Ordnung zu bieten hat.
Python nennt sich eine objektorientierte Programmiersprache. Das ist aber nur die halbe Wahrheit, denn als Multiparadigmen-Programmiersprache unterstützt sie insbesondere auch die funktionale Programmierung, und dies in immer mächtigerer Art und Weise. Nachdem sich die erste Folge dieser Reihe mit den Grundzügen der funktionale Programmierung beschäftigte, wird sich dieser Artikel den Techniken, Built-in-Funktionen und Bibliotheken rund ums funktionale Programmieren in Python widmen.
Python, zwar nicht als funktionale Sprache entworfen, nimmt doch immer mehr funktionale Charakteristiken an. Waren es anfangs nur First Class Functions, so fanden immer wieder neue funktionale Features Eingang in die Sprache:
- Closures
- anonyme Funktionen (Lambda-Funktionen)
- Generatoren und Generator-Ausdrücke.
- die klassischen Funktionen höherer Ordnung "map", "filter" und "reduce" Python
- List Comprehension
Closures
Closures oder innere Funktionen sind Funktionen, die beim Aufruf ihren Aufrufkontext konservieren. Diese spezielle Funktionen mit Gedächtnis sind eine beliebte Technik, um Dekoratoren in Python zu implementieren (siehe Rainer Grimm, "Dekoratoren in Python": Linux-Magazin 06/09, S. 96). Insbesondere ermöglichen Closures, Funktionen zu definieren, die innere Funktionen auf Anfrage zurückgeben. Für diese inneren Funktionen werden in Python gerne Lambda-Funktionen verwendet, denn durch sie können Closures sehr kompakt umgesetzt werden.
Ein klassischer Anwendungsfall für Closures und Lambda-Funktionen zugleich ist die Funktion "makefilter" aus Listing 1, die bei jedem Aufruf einen neuen Filter erzeugt. Vor dem Verständnis der Funktion "makefilter" steht zuerst das Verständnis der zwei funktionalen Komponenten Closures und Lambda-Funktionen.
Das kleine Beispiel "addWith" (Abbildung 1) zeigt, wie der Wert "first" beim Aufruf von "addWith" im Scope der Funktion gebunden wird, sodass auf ihn lesend aus der inneren Funktion "innerFunction" zugegriffen werden kann.

|
Abbildung 1: Lesender Zugriff auf den äußeren Scope.
|
Leider ist die Umsetzung von Closures unter Python 2 sehr eingeschränkt, da sich der umgebene Scope aus der inneren Funktion nur lesend referenzieren lässt (Abbildung 1).
Den schreibenden Zugriff auf eine Variable "start" aus der inneren Funktion "memorizeCount" quittiert der Interpreter mit einer schwer verständlichen Fehlermeldung (Abbildung 2). Durch den schreibenden Zugriff auf die Variable "start" in dem Ausdruck "start+=1" wird eine neue Variable in deren Scope angelegt. Bevor die Variable "start" einen Wert besitzt, wird aber versucht, diesen zu lesen. Dieser Zugriff muss scheitern.

|
Abbildung 2: Der schreibende Zugriff auf den äußeren Scope scheitert in Python 2.*
|
Mit dem neuen Schlüsselwort "nonlocal" erlaubt Python 3.0 auf den äußeren, nicht lokalen Scope schreibend zuzugreifen. Damit ist ein Zähler schnell implementiert.

|
Abbildung 3: Schreibender Zugriff auf den äußeren Scope mit Python 3.*
|
Lambda-Funktionen
Das zweite Bausteinchen für das Verständnis der Filter-Funktion (Listing 1) fehlt noch. Was sind Lambda-Funktionen? Lambda-Funktionen oder auch anonyme Funktionen sind Funktionskörper ohne Namen. Diese nehmen in Python eine beliebige Anzahl von Argumenten an und führen einen Ausdruck aus - zugleich der Rückgabewert der Funktion.
Lambda-Funktionen werden gerne im Zusammenhang mit Funktionen höherer Ordnung verwendet. Die Funktion "makefilter" aus Listing 1 ist eine First Class Function, denn sie gibt eine Funktion zurück, genauer die Lambda-Funktion "lambda s,a=allchars,d=delchars: s.translate(a, d)".
|
Listing 1: Filter-Funktion
|
def makefilter(keep):
""" Return a function that takes a string and returns a copy of that
string consisting only of the characters in 'keep'.
"""
import string
# make a string of all chars, and one of all those NOT in 'keep'
allchars = string.maketrans('', '')
delchars = ''.join([c for c in allchars if c not in keep])
# return the function
return lambda s,a=allchars,d=delchars: s.translate(a,d)
|
Aber nun von Anfang an: Die Funktion "makefilter" erzeugt bei jedem Aufruf der Funktion einen Filter, der nur die Buchstaben aus "keep" passieren lässt. Der Schlüssel zum Verständnis dieses Filters ist die Funktion "translate" aus der String-Bibliothek. "s.translate(a,d)" übersetzt einerseits alle Zeichen des Strings "s" entsprechend der Übersetzungstabelle "a" und löscht andererseits die Zeichen, die sich in "d" befinden. Nun ist die Übersetzungstabelle "a" in diesem konkreten Fall "string.maketrans('','')" die Identität und "d" das Komplement zu "keep". Das einzige fehlende Argument für die Lambda-Funktion "lambda s,a=allchars,d=delchars: s.translate(a,d)" ist der zu filternde String "s". Dieser String ist das Argument für die Filter-Funktion (Abbildung 4).

|
Abbildung 4: Die Filter-Funktion im Einsatz.
|
| Whitepaper |
|
Usage Landscape Enterprise Open Source Data Integration
Die Nachfrage nach Datenintegrationslösungen für Unternehmen ist zunehmend gestiegen und vor allem das Interesse an Open Source Technologien wird immer größer. Doch wie und von wem werden Open Source Datenintegrationslösungen genutzt und welches Nutzungsverhalten lässt sich daraus ableiten? Das vorliegende White Paper präsentiert die Erfahrungswerte von über 1000 Open Source Nutzern und liefert fundierte Antworten auf diese Fragen.
Download PDF (Registrierung erforderlich)
|
|
Daten Migration - Eine Publikation von Bloor Research
Datenmigrationsprojekte überschreiten häufig das Budget, neigen zu Verzögerung und werden unter Umständen komplett abgebrochen. Bloor Research ist eines der weltweit führenden IT-Forschungs-, Analyse- und Beratungsunternehmen und wird in dem vorliegenden White Paper die wichtigsten Aspekte dieser Problematik näher beleuchten. Ferner werden praktische Empfehlungen für erfolgreiche Migrationsprojekte gegeben, die Sie auf Ihr nächstes Projekt übertragen können.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|
Mathias Huber,
25.01.2010 17:31
hochmohr,
23.01.2010 19:52
def qsortFilter(L):
if len(L) <= 1: return L
return qsortFilter( filter(lambda lt: lt < L[0] , L[1:]) ) + L[0:1] + qsortFilter( filter(lambda gt: gt >= L[0] , L[1:]) )