log-in_small.gif (418 Byte)

LOG IN 21 (2001) Heft 2

 

Lieben Sie PYTHON?

  von Werner Arnhold

 

 "Warum das denn? Ich denke, man hatte sich gerade auf PASCAL geeinigt!"

 Reaktion eines Kollegenauf die Antwort "MODULA-2"zur Frage, in welcher Sprache denndas neue Projekt geschrieben werde (1988).

 

    Die Leute haben unterschiedliche Verhältnisse zu Programmiersprachen. Mancher sammelt sie wie andere Leute Briefmarken: "… was, Sie programmieren noch nicht in <allerneueste Sprache>?"
    Nicht selten stößt man damit im Kollegen-Team auf Widerstände. Häufig gibt es große Schwierigkeiten, Informatik-Kolleginnen und -Kollegen schon wieder für eine neue Unterrichtssprache zu begeistern.
    Andere haben zur Programmiersprache eher ein Verhältnis wie zur Muttersprache. "Ich habe als erste Sprache Deutsch gelernt, also denke ich in Deutsch, und warum sollte ich auch ohne Not eine andere Sprache benutzen?" Deutsch ist in diesem Satz leicht durch eine beliebige Programmiersprache zu ersetzen.

 


 Weshalb also PYTHON?

Was hat diese Sprache, was andere nicht haben?

Schauen wir nach:

 

Was hat PYTHON nicht, was andere haben?

    PYTHON hat wenig strenge Programmiererkontrollen. Philosophie: Der Programmierer soll gefälligst aufpassen, was er macht. Daraus ergeben sich einige Details:

 

 

 


Das Besondere an PYTHON

Sequenzen

    Kommen wir zu den Schmankerln der Sprache. Sequenz ist ein Sammelbegriff für Datentypen, die aus linear geordneten Elementen bestehen und auf die in identischer Weise zugegriffen wird. Standardmäßig gibt es hier Texte (string bzw. unicode), Tupel (tuple) und Listen (list). Listen sind änderbar, Texte und Tupel nicht, was jedoch keine große Einschränkung darstellt, wie wir gleich sehen werden. Schauen wir genauer hin:

Texte

    Texte sind lineare Folgen von Zeichen. Textkonstanten werden durch einfache Hochkommata ("'") bzw. Anführungsstriche (""") begrenzt. Der Text endet mit dem Begrenzer, mit dem er begann. So lässt sich auch das jeweils andere Zeichen im Text unterbringen (oder man maskiert sie mit einem Rückschräger). Das sieht dann so aus:

name = 'Emil'
fertigtext = "Das war's"
aufforderung = 'Antworten Sie nur mit "j" oder "n"'
protokoll = 'Der Text \'"j" oder "n"\' wurde ausgegeben'

    Unicode-Texte sehen genauso aus, nur dass sie ein kleines "u" vorangestellt bekommen. Es gibt ein umfangreiches Modul für ihre Bearbeitung, was aber auch erst so richtig interessant wird, wenn man die Zeichensatztabellen besitzt. Doch das führt etwas vom Thema ab. Was man mit Texten machen kann, dazu gleich mehr.

Tupel

    Ein Tupel ist eine irgendwie geartete Aufzählung von Datenobjekten, die noch nicht einmal gleichen Typs sein müssen. Sie werden einfach – durch Kommata getrennt – aufgezählt; wenn Verwechslungen möglich sind (z.B. bei Prozedurparametern), werden sie noch von runden Klammern eingeschlossen. Wenn man will, kann man immer welche setzen, das schadet nicht.

einkaufszettel = 'Milch', 'Butter', 'Gummibärchen'
zweiter_zettel = ('Brausepulver', 'Lakritz', 'Negerküsse')
vierelementiges_tupel = 1, 2.0, '3', ('1', 2, 'III', 4.0)
zweielementiges_tupel = 'a', 'b'
einelementiges_tupel = 3,
leeres_tupel = ()

Das vierelementige Tupel hat als letztes Element wieder ein Tupel aus vier Elementen.
Da Sequenzen auch links von Zuweisungszeichen auftauchen können, ergeben sich nette Varianten altbekannter kleiner Algorithmen:

untere = input('Untere Grenze: ')
obere = input('Obere Grenze: ')
if untere > obere:
    untere, obere = obere, untere

    So kann das Tauschen zweier Zahlen aussehen. Nett, nicht wahr? Der Unterschied zwischen input und raw_input ist übrigens, dass zwar beide eine Texteingabe von der Tastatur lesen, das erste aber gleich noch versucht, sie in ein PYTHON-Datenobjekt zu wandeln, hier also eine Zahl. Der Textparameter wird vor dem Warten auf die Benutzereingabe ausgegeben.

Listen

    Listen sind wie Tupel, nur mit zwei Unterschieden: Erstens müssen sie immer in eckigen Klammern notiert werden (weil sie sonst Tupel wären), und zweitens sind sie veränderbar. Das sieht so aus:

einkaufsliste = ['Milch', 'Butter', 'Gummibärchen']
einkaufsliste.append('Brausepulver')
vierelementige_liste = [1, [2.0], ('3',), ['1', 2, 'III', 4.0]]
zweielementige_liste = ['a', 'b']
einelementige_liste = [3]
leere_liste = []

Zugriffe

    Jetzt wird es hübsch! Der Zugriff auf einzelne Teil- elemente einer Sequenz geschieht – wie bei PASCAL-Arrays – über einen Index, der in eckigen Klammern steht; das ist noch nichts Besonderes. Das erste Element hat den Index 0. Der Zugriff auf nicht vorhandene Elemente löst eine "Exception" aus. Dennoch ist Folgendes legal:

print einkaufsliste[-1]

    Aber was bekommt man? Das letzte Element der Sequenz, hier also das zum Schluss angehängte Brausepulver. Das vorletzte Element erhält man über den Index –2, das vorvorletzte über –3 usw. Die Einkaufsliste hat also einen gültigen Indexbereich von –4 bis 3. Und weiter: Mit einer wunderschön einfachen Syntax können Sie Ausschnitte aus Sequenzen (so genannte slides) erzeugen:

meiereierzeugnisse = einkaufsliste[0:2]
suesskram = einkaufsliste[2:4]

    Ein Ausschnitt aus einer Sequenz wird durch zwei von einem Doppelpunkt getrennte Indizes beschrieben, von denen der erste die Nummer des ersten Ausschnittselements angibt und der zweite die auf das letzte Ausschnittselement folgende Nummer. Dabei spielt es keine Rolle, dass ein Element mit dem Index 4 nicht existiert. Überhaupt: Der Zugriff auf nicht existierende einzelne Elemente ist ein schwerer Fehler; das Erzeugen eines nicht existierenden Ausschnitts liefert einfach nur eine leere Sequenz. Die Ausschnittsschreibweise kennt übrigens auch Defaultwerte. Wenn wir den ersten Index weglassen, wird 0 angenommen, wenn wir den letzten weglassen, wird der Index nach dem des letzten Elements angenommen; wir bekommen also einen Ausschnitt bis zum Ende der Sequenz. Wir hätten unsere Sortierung des Einkaufszettels daher auch so schreiben können:

meiereierzeugnisse = einkaufsliste[:2]
suesskram = einkaufsliste[2:]

   Eine solche Schreibweise entbindet uns von der Notwendigkeit, die Länge der Sequenz zu kennen. Man kann auch beide Indizes weglassen; dann bekommt man eine Kopie der Sequenz, wohingegen man bei einer Zuweisung nur eine weitere Referenz auf das gleiche Objekt bekommt.

    Nun zur Nicht-Veränderbarkeit von Tupeln und Zeichenketten. Dies stört nicht – man bildet einfach Ausschnitte und setzt sie zusammen.

wort = 'zeile'
wort = wort[:2] + 'l' + wort[3:]
wort = wort[:4] + 'stoff' + wort[4:]

   Die erste Anweisung weist der Variablen den Text "zeile" zu, die zweite macht daraus "zelle", die dritte "zellstoffe". In jedem Fall wird der neue Text mit dem Variablennamen wort verknüpft. Die vorher damit verbundene Zeichenkette bekommt der Müllmann, wenn sonst keiner mehr eine Referenz darauf hat. Das ist flexibel genug. Mit Tupeln und Listen geht das ganz genau so.
    Was geht sonst noch mit Sequenzen? Sequenzen gleichen Typs können mit einem "+" zu einer neuen zusammengefügt werden; mit einem Multiplikationsoperator (kommutativ), der eine ganze Zahl mit einem Nicht-Zahl-Objekt verknüpft, lässt sich dieses Objekt zu einer Zeichenkette, einer Liste oder einem Tupel vervielfältigen. Hier ein kleines Zwiegespräch mit dem Interpreter (die Winkel ">>>" sind seine Eingabeaufforderung, der Interpreter zeigt die ausgewerteten Ausdrücke in der nächsten Zeile).

>>> 3 * 'h'
'hhh'

>>> 'h' * 3
'hhh'

>>> ('h',) * 3
('h', 'h', 'h')

>>> 3 * [7]
[7, 7, 7]

    Es werden also jeweils ein Text, ein Tupel bzw. eine Liste produziert. Was noch? Die Länge aller Sequenzobjekte lässt sich über die Prozedur len feststellen, Extremwerte über min und max.
    Listen als änderbare Sequenzen erlauben Einfüge-, Überschreib- und Löschoperationen auf Listen, ohne ein neues Objekt zu erzeugen. Man kann auch die Quicksort-Prozedur der Standard-C-Bibliothek unter dem Namen sort auf eine Liste anwenden. Wenn man eine selbst geschriebene Vergleichsprozedur mit dazuliefert, wird diese benutzt.
    Eine weitere feine Sache ist die Prozedur range. Sie liefert Listen von ganzen Zahlen und bekommt drei Parameter. Der erste gibt den Startwert an, der zweite den Endwert (der nicht mehr zur Liste dazu gehört), der dritte die Schrittweite. Fehlt er, wird 1 angenommen; gibt man nur einen Parameter an, wird er als Endwert betrachtet, der Startwert aber mit 0 angenommen.

>>> range(5)
[0, 1, 2, 3, 4]

>>> range(-5, 5)
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> range(-5, 5, 3)
[-5, -2, 1, 4]

   Hier kommt nun eine spezielle Schleifenform ins Spiel, die wie die Zählschleife von PASCAL heißt, aber eigentlich keine ist. Nennen wir sie Sequenz-Iteration, da sie elementweise durch eine Sequenz läuft. Sie verwendet das Schlüsselwort for. Beispiele:

>>> for z in 'Affe':
...             print string.swapcase(z),
...
a F F E

>>> gewinnzahlen = 2, 8, 12, 17, 23, 39, 14
>>> mein_tip = 1, 3, 5, 7, 9, 11
>>> treffer = 0
>>> for tip in mein_tip:
...             if tip in gewinnzahlen:
...                          treffer += 1
...
>>> print treffer
0

>>> endwert = 5
>>> for i in range(endwert):
...         print i,
...
0 1 2 3 4

    Das erste Beispiel zeigt den zeichenweisen Zugriff auf einen Text; eine aus dem String-Modul importierte Prozedur kippt Klein- in Großbuchstaben und umgekehrt. Das zweite Beispiel zeigt das elementeweise Abarbeiten zweier Tupel; das dritte das Abarbeiten einer Liste, die mit range erzeugt wurde. Da ist sie übrigens wieder, die gute alte Zählschleife! Nun werden Sie sagen: "Ja, wenn der Endwert sehr groß ist, dann ist das aber nicht sehr effizient, erst eine riesige Liste zu erzeugen, um dann die Zahlen nacheinander abzuarbeiten und hinterher die Liste wieder freizugeben. Effizienter ist es doch, eine Variable herzunehmen, die bei jedem Schleifendurchlauf um eins erhöht wird." Das dachte ich auch. Der Interpreter ist aber schlau genug, solche Fälle zu erkennen. Die Variante mit for und range läuft interessanterweise schneller als eine entsprechende vorprüfende Schleife.

Dictionaries

    Dieser Typ, auch als assoziatives Array bekannt, wird als Spezialfall der allgemeineren Gruppe der Mappings bezeichnet, deren einziger Vertreter er aber (noch) ist. Man kann sich ein Dictionary ein wenig wie einen PASCAL-Record vorstellen, nur dass zur Laufzeit auch noch die Feldbezeichner verfügbar sind und verändert oder neue hinzugefügt werden können.

    Die Feldbezeichner heißen hier Schlüssel; sie verweisen auf Werte. Ein Dictionary ist also eine Menge von Schlüssel-Wert-Paaren. Es wird in geschweiften Klammern notiert; über die Schlüssel wird auf die Werte zugegriffen.

komponisten = {
    'A' : ['Abaco'],
    'B' : ['Bach', 'Beethoven', 'Brahms'],
    'C' : ['Chatschaturian', 'Chopin', 'Corelli'],
    'D' : ['Debussy', 'Distler', 'Dukas']
}
komponisten['E'] = ['Eisler']

    Dies ist ein kleines Beispiel für einen indexsequenziell geordneten Datenbestand. Ein neues Schlüssel-Wert-Paar wird einfach über eine Zuweisung erzeugt. Zugegriffen wird über den Schlüssel (in gleicher Syntax wie bei Sequenzen).

schluessel = suchwort[0]
if komponisten.has_key(schluessel) and suchwort in komponisten[schluessel]:
    print 'Treffer'

    Im suchwort sei ein zu suchender Komponist gespeichert. Mit Zugriff auf das erste Zeichen (Index 0) wird der Anfangsbuchstabe ermittelt und in schluessel aufgehoben. Da der Zugriff über einen nicht existierenden Schlüssel zu einer Ausnahme führt, muss die Existenz des Schlüssels vorher mit has_key überprüft werden. Über den Schlüssel erhält man dann in diesem Fall eine Liste, in der mit dem Operator in festgestellt werden kann, ob das Suchwort enthalten ist.
    Dies ist ein "zahmes" Dictionary, da Schlüssel und Werte jeweils immer vom gleichen Typ sind. Das ist aber nicht notwendig. Als Schlüssel ist jedes nicht änderbare Objekt zulässig, also z.B. auch ganze oder Gleitkommazahlen und Tupel.

    Von einem Dictionary erhält man mit dem Aufruf von keys() eine Liste aller Schlüssel, mit values() eine Liste aller Werte. Seit Version 2.2 kann man die Sequenz-Iteration auch bei Dictionaries direkt anwenden; sie arbeitet dann auf der Schlüsselliste.
    Die Elemente in einem Dictionary haben keine verlässliche Reihenfolge, wer dennoch eine haben will, muss die Schlüsselliste selbst sortieren.

Klassen

    Klassen können Daten und Prozeduren (Methoden) enthalten. Daten sind der Instanz zugeordnet; seit Version 2.2 gibt es aber auch Klassenprozeduren, ähnlich den in C++ als static definierten. Neben frei wählbaren Prozedurnamen gibt es noch spezielle, die durch zwei führende und abschließende Unterstriche gekennzeichnet sind. Mit ihnen lassen sich Konstruktoren (__init__) oder Destruktoren (__del__) erzeugen; für letztere ist durch die automatische "garbage collection" allerdings selten Bedarf. Außerdem gibt es die Möglichkeit, Prozeduren wie __add__ oder __lt__ zu definieren, um Instanzen mit einem "+" oder "<" verknüpfen zu können. Mit der Definition von __getitem__ und __setitem__ ist der lesende und schreibende Zugriff auf eine Instanz mit einem indexartigen Wert in der Notation von Sequenzen oder Dictionaries mit eckigen Klammern möglich. Grundsätzlich lassen sich so alle Operatoren für eigene Klassen anwenden.

Schauen wir uns ein Beispiel an:

class Uhrzeit:
    def __init__(self, s, m):
        self.stunde = s
        self.minute = m

    def setzen(self, s, m):
        self.stunde = s
        self.minute = m

    def __str__(self):
        return '%02d:%02d' % (self.stunde, self.minute)

zeit = Uhrzeit(8, 15)

    Hier eine Klasse für Uhrzeiten. Es gibt einen Konstruktor (__init__), eine Prozedur mit selbstgewähltem Namen und eine, die eine String-Repräsentation des Datenbestandes der Instanz liefert, wenn man die Instanz z.B. mit print zeit aufruft. Wir sehen hier die übliche Syntax für die Rückgabe von wertliefernden Prozeduren am Beispiel einer Textwandlung im "printf"-Stil der Standard-C-Bibliothek. Da alle PYTHON-Datenobjekte außer Integern und Gleitkommazahlen offenbar als Verweise implementiert sind, ist übrigens jedes Objekt als Rückgabewert einer Prozedur möglich.

    Alle Instanzprozeduren führen in ihrer Parameterliste als ersten den Verweis auf die Instanz selbst (self) und spechen innerhalb der Klassendefinition alle internen Datenobjekte und Prozeduren immer als self.<Objekt> an. Außerhalb der Klassendefinition, also beim Gebrauch der Instanzen, fällt das self weg, sowohl beim Aufruf der Prozeduren, wo es vom Interpreter implizit mit übergeben wird, als auch beim Zugriff auf Instanzvariablen. Die Bestandteile der Instanz zeit wären hier also als zeit.stunde und zeit.minute zugreifbar.

    Generische Prozeduren wie in C++ oder JAVA gibt es nicht. Das liegt daran, dass auch Prozedurnamen wie Variablennamen nach einmaliger Vergabe nicht blockiert sind, sondern durch die nächste Zuweisung bzw. Prozedurdefinition einfach neu verknüpft werden. Nach der (syntaktisch legalen) Definition von drei Prozeduren gleichen Namens mit unterschiedlich vielen Parametern hat sich der Interpreter unter dem Prozedurnamen lediglich die letzte Version gemerkt, die anderen sind überschrieben. Auch das Unterscheiden anhand von Parametertypen geht nicht, da Parameter nur ihrer Anzahl nach festgelegt sind.

    Dennoch lassen sich ähnliche Effekte durchaus erzielen. PYTHON erlaubt es zum einen, Prozedurparametern bei der Definition Defaultwerte zuzuweisen. Beim Aufruf können dann diese Parameter (die am Ende der Parameterliste   liegen müssen) weggelassen werden. Hier noch einmal die Uhrzeitklasse:

from time import time, localtime
class Uhrzeit:
    def __init__(self, s = -1, m = -1):
        if s == -1:
            self.stunde = localtime(time()) [3]
        else:
            self.stunde = s
        if m == -1:
            self.minute = localtime(time()) [4]
        else:
            self.minute = m

    Hier werden zwei – offenbar unsinnige – Defaultwerte festgelegt, um erkennen zu können, wann kein aktueller Parameter übergeben wurde. In diesem Fall wird mit der Prozedur localtime (Standard-C-Bibliothek), die die aufgespaltene Betriebssystemzeit als Tupel liefert, die Stunde bzw. Minute aus der Maschine geholt und zugewiesen. Damit sind folgende Aufrufe möglich:

zeit1 = Uhrzeit(8, 15)
zeit2 = Uhrzeit()
zeit3 = Uhrzeit(7)

   Hier hat zeit1 den Wert 8 Uhr 15, zeit2 die aktuelle Uhrzeit. Im Fall von zeit3 wird nur ein aktueller Parameter übergeben, der wird dem ersten formalen Parameter, also s zugeteilt, für m wird dann –1 gesetzt. Wir erhalten eine Uhrzeit mit 7 als Stundenwert und der aktuellen Minutenzahl. Das Umgekehrte geht auch, aber mit einem anderen Mechanismus. Wir geben einfach an, welcher formale Parameter den aktuellen Wert bekommen soll:

zeit4 = Uhrzeit(m = 27)

    Jetzt erhalten wir die aktuelle Stunde, für die Minute aber den übergebenen Wert. Diese Übergabemethode ist extrem wertvoll für den Aufruf im Bereich der Programmierung grafischer Oberflächen, wo man ja immer Tausenderlei einstellen kann. Die Prozedur definiert für alle möglichen Dinge vernünftige Defaultwerte, und wenn ich keine Parameter angebe, bekomme ich schon mal etwas Brauchbares, aber eben mit Stan- dardaussehen. Ich kann dann über die benannte Parameterübergabe gezielt die Werte beeinflussen, die ich möchte und brauche mich um die anderen nicht zu kümmern. Nebenbei gesagt, ist es noch möglich, Prozeduren zu definieren, die den Aufruf mit beliebig vielen Parametern ermöglichen.

Zugriffsschutz

    Umfangreiche Schutzkonzepte mit Spezifizierern wie public, protected oder friend und private gibt es in PYTHON nicht. Der Interpreter betrachtet den Programmierer einfach als vertrauenswürdige Person. Auf Modulebene (ein Modul ist der Inhalt einer PYTHON-Quelldatei) kann man Datenobjekte ansatzweise verstecken, indem man ihre Namen mit einem Unterstrich beginnen lässt. Sie werden dann bei einem globalen Import (from Modul import *) nicht mit importiert. Wenn man ihre Namen jedoch kennt und explizit importiert, sind sie sichtbar.

    Eine etwas stärkere Einschränkung ist bei Klassen möglich. Prozeduren und Daten, deren Bezeichner mit zwei Unterstrichen beginnen, sind von außen nicht zugreifbar.

class A:
    def __private_proz(self):
        self.__privater_wert = 999
        print 'Diese private Prozedur ist nur innerhalb benutzbar'

    def oeffentliche_proz(self):
        print 'Diese öffentliche Prozedur startet die private:'
        self.__private_proz()
        print 'Der private Wert ist', self.__privater_wert

a = A()
a.oeffentliche_proz()
a.__private_proz()
print a.__privater_wert()

Ein Interpreterlauf mit diesem Programm liefert:

Diese öffentliche Prozedur startet die private:
Diese private Prozedur ist nur innerhalb benutzbar
Der private Wert ist 999

Traceback (most recent call last):
    File "tst.py", line 54, in ?
        a.__private_proz()
AttributeError: A instance has no attribute '__private_proz'

    Wie wir sehen, sind die private Prozedur und der private Wert innerhalb der Klasse verfügbar, außerhalb führt dies jedoch zu einer Fehlermeldung. Die letzte Zeile wird daher gar nicht mehr ausgeführt, würde aber eine ähnliche Meldung hervorrufen.

Vererbung

    In PYTHON darf selbstverständlich geerbt werden. Die Erblasserklasse steht einfach im Klassenkopf in Klammern hinter dem Klassennamen:

class Genauere_Zeit(Uhrzeit):
    def __init__(self, std, min, sec):
        Uhrzeit.__init__(self, std, min)
        if sec == -1:
            self.sekunde = localtime(time())[5]
        else:
            self.sekunde = sec

    def __str__(self):
        return Uhrzeit.__str__(self) + ':%02d' % self.sekunde

    So sieht das dann aus. Man sieht, dass der Programmierer sich selbst um den Aufruf des Konstruktors der Basisklasse kümmern muss. In diesem Fall muss er auch den Verweis self mit übergeben, genauso wie bei der überladenen Prozedur __str__. Wäre sie hier nicht neu definiert, würde ihr Aufruf nur Stunde und Minute liefern.

Mehrfache Vererbung ist möglich. Syntaktisch ist das ganz einfach:

class Abgeleitete(Basis1, Basis2, Basis3):
    def __init__( ....

    Was geschieht, wenn ein Name in der abgeleiteten Klasse vorkommt, der genauso in Basisklassen oder deren Unterklassen existiert? Die Auflösungsregel sagt: Zuerst wird in der abgeleiteten Klasse gesucht, dann in der Klasse Basis1, dann rekursiv in deren Unterklassen, dann erst in Basis2, dort rekursiv hinunter usw. Beim ersten Treffer ist die Suche beendet. Auch hier gilt wieder: Der Programmierer soll sich selbst kümmern. Er muss solche Fälle durch geeignete Namensgebung zu vermeiden trachten. Immerhin ergibt sich kein Problem, wenn zwei der Basisklassen sich wiederum von einer gemeinsamen Unterklasse ableiten. Die entsprechenden Komponenten werden nicht zweifach erzeugt, stattdessen wird ein und derselben Variablen eben zweimal der gleiche Wert zugewiesen.
    Aus den hier auftauchenden Problemen gibt es nur den Ausweg der Disziplin des Programmierers und eventuell den Versuch, den Systementwurf noch mal zu überdenken.
    Eine kleine, aber feine Neuerung gibt es noch ab Version 2.2: Typen wie Dictionaries oder Listen lassen sich als Basisklassen verwenden. Dies ergibt einen viel stringenteren Aufbau der Sprache, was ein Unterrichtender sicher zu schätzen weiß.

Weiteres

    Einiges gibt es noch zu erwähnen: Neben kleinen Nettigkeiten (die einzige Sprache, die ich kenne, die einen Ausdruck wie "0 <= tag <= 31" ermöglicht) hat PYTHON eine vernünftige Ausnahme-Behandlung; es ist möglich, Teile des Interpreters zur Laufzeit für allerhand hinterlistige Zwecke zu verwenden. Der PYTHON-Interpreter kann in Programme anderer Sprachen eingebunden werden; es gibt eine auf JAVA basierende Version (J-PYTHON), und es gibt eine gut dokumentierte Standard-Schnittstelle zu C. Und damit kommen wir zu einem wichtigen Punkt.

Bibliotheken

    PYTHON ist nicht nur eine kleine, aber mächtige Sprache, es ist durch seine C-Schnittstelle auch ein Büchsenöffner, mit dem sich ein leichter Zugang zu all der vielen Software ergibt, die in Dosen von C-Bibliotheken vorliegt. So viel davon ist bereits in der Standarddistribution enthalten, dass es schwer fällt, dies hier knapp aufzuzählen. Versuchen wir es: Auf die gesamte mächtige Standard-C-Bibliothek kann zugegriffen werden; es gibt Unterstützung für Netzwerkprogrammierung in verschiedenen Abstraktionsniveaus, Threads, diverse Datenbankinterfaces, Kompressions- algorithmen, Schnittstellen zu allen wesentlichen Internet-Diensten, Unterstützung für CGI-, XML-, HTTP-Programmierung, Multimedia, Kryptografie, Parsing, Debugging, Profiling und vieles mehr. Die Zahl der in der Standarddistribution vorhandenen Bibliotheken wächst von Version zu Version. Es gibt Schnittstellen zu spezifischen GUI-Bibliotheken für Windows, LINUX (u.a. gtk und Qt) sowie das bereits erwähnte Tk-Interface, das auf fast allen Plattformen läuft. Es gibt quasi nichts, was modern und chic ist, was Sie mit PYTHON nicht machen könnten. Auch die Zahl der außerhalb der Distribution erhältlichen Software ist beträchtlich und wächst ebenfalls rapide.

 


Also: Weshalb PYTHON?


Werner Arnhold
Freie Universität Berlin
Fachbereich Erziehungswissenschaft und Psychologie
Gemeinsame Einrichtung Datenverarbeitung und informatische Bildung (GEDIB)
Arbeitsbereich "Lehrerfortbildung"
Habelschwerdter Allee 45
14195 Berlin

E-Mail: warnhold@zedat.fu-berlin.de

 


Literatur/Internetquellen

Heimatseite von Python (auch zum Herunterladen des Interpreters):http://www.python.org/ (Stand: November 2001).

Löwis, M. v.; Fischbeck, N.: Python 2 – Einführung und Referenz der objektorientierten Skriptsprache. Reihe "Professionelle Programmierung". München u.a.: Addison-Wesley, 22001.

Lutz, M.; Ascher, D.: Einführung in Python. Beijing u. a.: O’Reilly, 2000.

Python-Anwendersoftware für diverse Fachgebiete:http://www.vex.net/parnassus/ (Stand: November 2001).