Modulare Programmierung

Was werden wir behandeln?
  • Was Module ungefähr sind
  • Funktionen als Module
  • Verwendung von Modul-Dateien
  • Das Schreiben eigener Funktionen und Module
  • Was ist ein Modul?

    Das vierte Element der Programmierung ist die modulare Programmierung. Tatsächlich ist diese nicht unbedingt erforderlich, und wenn du verwendest, was wir bisher durchgenommen haben, dann kannst du schon recht nette, eindrucksvolle Programme schreiben. Dennoch, wenn die Programme größer werden, wird es immer schwerer und schwerer den Überblick zu behalten, über das, was wo abläuft. Wir benötigen dringend eine Möglichkeit einige Einzelheit weg zu abstrahieren, so dass wir allein über unser zu lösendes Problem nachdenken müssen, nicht über minutiöse Einzelheiten, wie der Computer arbeitet. In einem gewissen Umfang ist es das, was Python, BASIC etc. schon für uns mit ihren eingebauten Fähigkeiten tun - sie bewahren uns davor, uns mit der Hardware des Computers abgeben zu müssen, wie das Lesen der einzelnen Tasten der Tastatur usw.

    Die Rolle der modularen Programmierung ist es, dem Programmierer zu ermöglichen, die eingebauten Fähigkeiten der Programmiersprache zu erweitern. Dabei werden Programmstücke innerhalb von Modulen eingepackt, die wir dann in unsere Programme 'einstecken' können. Die erste Form von Modul war das Unterprogramm (Subroutine), welche eine Codeblock war, zu dem man hinspringen konnte (vergleichbar dem GOTO, das im Kapitel über Verzweigungen erläutert wurde); wenn der Block erledigt war, wurde wieder zu der Stelle zurückgesprungen, von der es aufgerufen wurde. Dieser besondere Stil der Modularität ist bekannt als Prozedur oder Funktion. In Python und einigen anderen Sprachen hat das Wort Modul eine spezifische Bedeutung, die wir kurz betrachten werden, aber lass uns zuvor die Funktionen etwas näher anschauen.

    Der Gebrauch von Funktionen

    Bevor die Erzeugung von Funktionen erläutert wird, lass uns untersuchen, wie wir die vielen, vielen Funktionen verwenden können, die bei jeder Programmiersprache dabei sind (oftmals Bibliothek oder Library genannt).

    Wir haben schon einige Funktionen im Gebrauch und andere im Abschnitt über die Operatoren aufgelistet. Jetzt werden wir beleuchten, was diese gemeinsam haben und wie wir sie in unseren Programmen nutzen können.

    Die Basisstruktur einer Funktion ist wie folgt:

    einWert = irgendeineFunktion(einArgument, einanderes, usw...)
    

    Dies ist eine Variable, die einen Wert durch einen Funktionsaufruf erhält. Die Funktion kann 0 oder viele Argumente akzeptieren, die sie wie interne Variablen behandelt. Funktionen können intern andere Funktionen aufrufen. Lass uns ein paar Beispiele in unseren verschieden Sprachen betrachten, um zu sehen, wie das funktioniert:

    BASIC: MID$(str$,n,m)

    Dies druckt die folgenden m Zeichen beginnend beim nten in str$. (Erinnere dich, dass Namen, die in BASIC mit '$' enden, einen String kennzeichnen.)

    time$ = "MORGEN MITTAG ABEND"
    PRINT "Guten";MID$(time$,7,7)
    

    Dies druckt "Guten MITTAG" aus.

    BASIC: ENVIRON$(str$)

    Dies gibt die spezifizierte Umgebungsvariable str$ zurück.

    PRINT ENVIRON$("PATH")
    

    Druckt den momentanen, in DOS gesetzten PATH (gewöhnlich über die autoexec.bat-Datei) aus.

    Tcl: llength L

    Gibt die Länge der Liste L zurück.

    set a {"Erster" "Zweiter" "Dritter"} # 3 Element-Liste
    puts [llength $a]  # die Ausgabe ist '3'
    

    Anmerkung: Nahezu alles in Tcl ist eine Funktion (oder wie Tcl es zu bezeichnen pflegt, ein Kommando). Dies führt zu einer grausigen Syntax , macht es aber dem Computer besonders leicht, Tcl-Programme zu lesen. Dies ist wichtig, weil Tcl für den Ausdruck Tool Control Language (Werkzeugkontrollsprache) steht und entwickelt wurde, um in andere Programme als Makrosprache eingebettet zu werden, wie Visual Basic for Applications(VBA) in Microsoft-Produkten. Du kannst dies auch in Python auf dieselbe Art einbetten, aber Tcl ist einzigartig in dem, wozu es geschaffen wurde, nämlich wegen des Einbettens.

    Python: pow(x,y)

    x = 2   #  wir werden 2 als unsere Basiszahl verwenden
    for y in range(0,11):
       print pow(x,y)    # berechnet 2 hoch y, hier 0-10
    

    Hier generieren wir Werte von y mit den Werten 0 bis 10 und rufen die eingebaute pow()-Funktion mit 2 weitergegebenen Argumenten auf: x und y. Bei jedem Aufruf werden die momentanen Werte von x und y innerhalb des Aufrufes ersetzt und das Ergebnis wird gedruckt.

    Anmerkung: Der Exponentialoperator ** ist äquivalent zur pow()-Funktion.

    Python: dir(m)

    Eine andere nützliche in Python eingebaute Funktion ist dir, die beim Weitergeben eines Modulnamens eine Liste der gültigen Namen zurückgibt - meist der Funktionen - in diesem Modul. Probier es mit eingebauten Funktionen:

    print dir(__builtin__)
    

    Anmerkung: Um dies auf beliebige andere Module anwenden zu können, mußt du das Modul zuerst mit import importieren, andernfalls wird Python bemängeln, dass es den Namen nicht zuordnen kann.

    Bevor wir etwas anderes machen werden, sollten wir jetzt besser mehr im Detail über Python-Module reden.

    Der Gebrauch von Modulen

    Python ist eine extrem erweiterungsfähige Sprache (genauso wie Tcl), in die du zusätzliche Fähigkeiten durch den import von Modulen einbringen kannst. Wir werden in Kürze sehen, wie man Module erzeugt, aber jetzt werden wir mit einigen Standardmodulen herumspielen, die mit Python ausgeliefert werden.

    sys

    Wir haben sys schon getroffen, als wir es für exit aus Python verwendet haben. Es beinhaltet ein ganzes Bündel mit noch anderen nützlichen Funktionen. Um diese zugänglich zu machen, müssen wir ein import sys durchführen:

    import sys # macht Funktionen zugänglich
    sys.exit() # Präfix 'sys.'
    

    Falls wir wissen, dass wir die Funktionen eines Moduls sehr oft verwenden werden und wir sonst nicht die gleichen Funktionsnamen importiert oder erzeugt haben, dann können wir dies tun:

    from sys import *  # importiert alle Namen von sys 
    exit() # kann jetzt ohne spezifizierendes Präfix 'sys' benutzt werden
    

    Andere Module und was sie beinhalten

    Wir könne alle Pythonmodule auf diese Art und Weise importieren und verwenden, und das betrifft auch die von dir selbst erzeugten . Wir werden bald sehen, wie man das macht. Zuerst aber machen wir eine schnelle Tour durch einige Python-Standardmodule und etwas von dem, was sie zu bieten haben:

    Modulname Beschreibung
    sys Erlaubt die Interaktion mit dem Pythonsystem:
  • exit() - Exit!
  • argv - greift aud die Kommandozeilenargumente zu
  • path - greift auf den Sytempfad zu
  • ps1 - ändert das '>>>' Pythonprompt!
  • os Erlaubt die Interaktion mit dem Betriebssystem:
  • open - öffnet eine Datei
  • system - führt einen Systembefehl aus
  • mkdir - erzeugt ein Verzeichnis (directory)
  • getcwd - findet das momentane Arbeitsverzeichnis
  • string Erlaubt die Manipulation von Strings
  • atoi/f/l - konvertiert string nach integer/float/long
  • find - findet Substring
  • split - zerlegt String in 'Wörter'
  • upper/lower - ändert Groß- in Kleinschreibung und umgekehrt
  • re Erlaubt die Manipulation von Strings in reguläre Ausdrücke
    im Unix-Stil
  • search - findet Muster irgendwo im String
  • match - findet nur Stringanfänge
  • split - zerteilt durch Muster getrennte Felder
  • sub,subn - Ersetzen von Strings
  • math Erlaubt den Zugang zu vielen mathematischen Funktionen:
  • sin,cos etc - trigonometrische Funktionen
  • log,log10 - natürlicher und Zehnerlogarithmus
  • ceil,floor - ganzzahliges Abrunden nach oben, unten
  • pi, e - Kreiszahl und Eulerzahl
  • time Zeit-(und Datums-) Funktionen
  • time - ergibt die momentane Zeit (in Sekunden ausgedrückt)
  • gmtime - konvertiert Zeit in Sek. nach UTC (GMT)
  • localtime - konvertiert in Lokalzeit
  • mktime - Umkehrung von Lokalzeit
  • sleep - unterbricht das Programm für n Sekunden
  • Diese ist nur die Spitze des Eisberges. Python ist tatsächlich mit Dutzenden von Modulen ausgestattet, und du kannst auch viele runterladen. (Eine gute Quelle ist Vaults of Parnassus.) Sieh dir die Dokumentationen an und finde etwas heraus über Internet-Programmierung, Graphik, Aufbau von Datenbanken etc.

    Als wichtige Sache sollte man sich vergegenwärtigen, nämlich dass die meisten Programmiersprachen diese Grundfunktionen eingebaut oder als Teil ihrer Standardbibliothek besitzen. Überprüfe die Dokumentation, bevor du eine Funktion schreibst - es könnte sie schon geben! Dies führt uns fein zum...

    Definieren eigener Funktionen

    OK, wir wissen jetzt, wie man existierende Funktionen verwendet, aber wie erzeugen wir eine neue Funktion? Einfach indem wir sie deklarieren. Das geschieht durch das Schreiben einer Anweisung, die dem Interpreter erzählt, dass wir einen Codeblock definieren wollen, den es überall in unserem Program auf Verlangen einsetzt.

    So lass uns einmal eine Funktion erzeugen, die eine Multiplikationstabelle für jedes von uns vorgegebene Argument ausdruckt. In BASIC sieht das so aus:

    SUB VIELFACH (N%)
    FOR I = 1 TO 12
        PRINT I; "x"; N%; "="; I * N%
    NEXT I
    END SUB
    

    Und wir können das aufrufen mit:

    PRINT "Hier ist die 7er Tabelle..."
    VIELFACH(7)
    

    Anmerkung: Wir definierten einen Parameter , genannt N% und übergaben ihm das Argument 7 . Die lokale Variable N% innerhalb der Funktion nimmt den Wert 7 an, wenn wir sie aufrufen. Wir dürfen so viele Parameter in der Funktionsdefinition definieren, wie wir möchten und die aufrufenden Programme müssen für jeden Parameter Werte vorgeben. Einige Programme erlauben es dir, Ersatzwerte (Defaultwerte) für einen Parameter vorzugeben, so daß, falls kein anderer Wert angeben wird, die Funktion den Ersatz nimmt. Wir werden dies später in Python sehen.

    In Python sieht die VIELFACH-Funktion so aus:

    def vielfach(n):
        for i in range(1,13):
            print "%d x %d = %d" % (i, n, i*n)
    

    Und wird so aufgerufen:

    print "Hier ist die 9er Tabelle..."
    vielfach(9)
    

    Beachte, dass diese Funktionen keine Werte zurückgeben (Sie sind in Wirklichkeit das, was einige Sprachen Prozeduren nennen). Deshalb kannst du feststellen, dass die BASIC-Version tatsächlich das Schlüsselwort SUB anstelle von FUNCTION verwendet. Dies steht für Subroutine, ein aus der Zeit der Assemblerprogrammierung verwendeter Begriff, der in BASIC eine Funktion meint, die keine Werte zurückgibt. Im Gegensatz dazu verwendet Python den Begriff def, der die Kurzform für 'define' (definiere) , so dass folglich angenommen wird, dass es sich um eine Funktion handelt.

    Erinnerst du dich, dass ich den Gebrauch von Defaultwerten erwähnt habe? Einen sinnvollen Gebrauch erfolgt in einer Funktion, die den Wochentag zurückgibt. Wenn wir sie ohne Wert aufrufen, meinen wir den heutigen Tag, andernfalls übergeben wir eine Tageszahl. Etwa so:

    # TagesZahl von  -1 => heute
    def wochenTag(TagesZahl = -1):
        tage = ['Montag','Dienstag',
                'Mittwoch','Donnerstag', 
                'Freitag', 'Samstag', 'Sonntag']
                    
        # überprüfe den Ersatzwerte        
        if TagesZahl == -1:
            # Verwende die time-Module-Funktionen um die momentane Zeit zu erhalten
            # siehe obige Tabelle und die offizielle Modul-Dokumentation
            import time
            diezeit = time.localtime(time.time())
            TagesZahl = diezeit[6]
        return tage[TagesZahl]
    

    Anmerkung: Wir benötigen das time-Modul nur dann, wenn der Defaultwert in Anspruch genommen wird; deshalb verschieben wir die Import-Operation , bis wir sie brauchen. Dies führt zu einer leichten Ausführungsbeschleunigung, wenn wir die Defaultwertfähigkeit unsererer Funktion nicht verwenden.

    Jetzt können wir dies aufrufen mit:

    print "Heute ist: %s" % wochenTag()
    # erinnere dich, dass wir in der Computersprache mit 0 zu zählen beginnen
    # und in diesem Falle nehmen wir den Montag als ersten Tag an.
    print "Der dritte Tag ist %s" % wochenTag(2)
    

    Wieder zurück zur Multiplikation....

    Was ist, wenn wir eine Funktion definieren möchten, die die Werte der Multiplikation als ein Feld (Array) zurückgibt? So sieht das in BASIC aus:

    FUNCTION VIELFACH% (N%)
        DIM WERTE(12) AS INTEGER
        FOR I = 1 to 12
            WERTE(I) = I*N%
        NEXT I
        RETURN WERTE
    END FUNCTION
    

    Und in Python:

    def vielfach(n):
        # erzeuge neue leere Liste
        werte = []  
        for i in range(1,13):
            werte.append(i*n)
        return werte
    

    Das wäre ziemlich dumm, weil es einfacher ist, direkt auf Wunsch i*n zu berechnen. Aber hoffentlich erkennst du die Idee. Eine praktischere Funktion, die einen Wert zurückgibt, mag eine sein, die die Anzahl der Wörter in einem String zählt.Du kannst sie verwenden, um die Wörter in einer Datei zu zählen, durch addieren der Gesamtzahlen in jeder Zeile.

    Der Code dafür könnte etwa so aussehen:

    def numworte(s):
        list = split(s) # Liste mit jedem Wort als Element
        return len(list) # Rückgabe  der Elementzahl in der Liste
    
    for line in file:
        total = total + numworte(line) # accumuliert Gesamtzahl für jede Zeile
    print "Die datei hat %d Wörter" % total
    

    Wenn du das jetzt ausprobierst, merkst du, dass dies nicht funktioniert. Ich haben hier eine allgemeine Design-Technik verwandt, die lediglich skizziert, wie meines Erachtens der Code in etwa aussehen könnte und es nicht schwer macht, daraus den absolut korrekten Code zu erstellen. Dies ist manchmal bekannt als Pseudo Code oder in einem etwas formaleren Stil Program Description Language (PDL:"Programmbeschreibungssprache").

    Bald werden wir eine etwas nähere Betrachtung vom Umgang mit Dateien und Strings haben; etwas später in diesem Kurs werden wir auf dieses Beispiel zurückkommen und es richtig machen.

    Tcl Functionen

    Wir können natürlich Tcl-Funktionen erzeugen, und wir tun das unter Verwendung des proc Kommandos, etwa so:

    proc vielfach {m} {
        for {set i 1} {$i <= 12} {incr i} {
            lappend results [expr $i * $m]
    	}
        return $results
    }
    

    Beachte, dass ich automatisch eine Liste mit dem Namen results erzeuge, sobald ich das Tcl-lappend verwende.

    Ein Wort zur Warnung

    Tcl ist ein bisschen anders in der Art wie es mit Funktionen umgeht. Sicherlich hast du schon festgestellt, dass ich die eingebauteten Funktionen Kommandos genannt habe. Das ist deshalb so, weil in Tcl jedes beim Promptzeichen eingegebene Kommando tatsächlich ein Funktionsaufruf ist. Die meisten Sprachen kommen mit einem Satz von Schlüsselwörtern (keywords), wie for, while, if/else und so weiter daher. Tcl macht alle diese Konrollschlüsselwörter und Kommandos zu Funktionen. Das hat den interessanten, verwirrenden und sehr starken Effekt, uns zu erlauben, eingebaute Kontrollstrukturen etwa wie folgt umzudefinieren:

    set i 3
    while {$i < 10} {
        puts $i
        set i [expr $i + 1]
        }
    

    Wie erwartet, druckt dies die Ziffern von 3 bis 9 (= 1 weniger als 10) aus. Aber lass uns jetzt unsere eigene Version des while-Kommandos redefinieren:

    proc while {x y} {
      puts "Mein while jetzt"
    }
    
    set i 3
    while {$i < 10} {
        puts $i
        set i [expr $i + 1]
        }
        
    

    Dies tut nichts ausser lediglich die Meldung "My while now" zu drucken. Der Ausdruck und die Befehlsabfolge werden ignoriert, weil TCL sie als Parameter der while-Funktion behandelt und die while-Funktion erwartet sie, aber ignoriert sie! Du kannst also sehen, wie wir Prozeduren in Tcl definieren und wie wir dies missbrauchen können, um sehr verwirrende Programme zu erzeugen - tue dies nicht, ohne einen äußerst guten Grund zu haben!

    Die Erzeugung unserer eigener Module

    Wir können jetzt also unsere eigenen Funktionen erstellen und diese von anderen Stellen in unserem Programm aus aufrufen. Das ist deshalb gut, weil es uns eine Menge an Tipparbeit erspart und, viel wichtiger, unsere Programme viel leichter verständlich macht, weil wir einiges über die Details vergessen können, nachdem wir die Funktion erschaffen haben, die sie versteckt. (Das Prinzip des Verpackens von komplexen Teilen eines Programmes innerhalb einer Funktion wird aus ziemlich offensichtlichen Gründen als Verstecken von Information bezeichnet.) Aber wie können wir diese Funktionen in anderen Programmen verwenden? Wir erzeugen ein Modul.

    Python-Module

    Ein Modul in Python ist nichts Besonderes. Es ist lediglich eine schlichte Textdatei voll mit Python-Programmanweisungen. Normalerweise sind diese Anweisungen Funktionsdefinitionen. Das ist so, wenn wir eingeben:

    from sys import *
    

    Im Endeffekt kopieren wir die Inhalte von sys.py in unser Programm hinein, genau so wie mit einer Ausschneide-und-Einfüge-Operation. (Es ist nicht genau, aber vom Konzept her so.) In der Tat kopiert in einigen Programmiersprachen (bemerkenswerterweise C++) der Übersetzer einfach direkt Moduldateien in das laufende Programm, wenn diese erforderlich sind.

    Also zum Rekapitulieren: Wir erzeugen ein Modul dadurch, indem wir eine Python-Datei erzeugen, die die Funktionen enthält, die wir in anderen Programmen wiederverwenden wollen. Dann importieren wir unser Modul genauso, wie die Standardmodule. Einfach, nicht? Dann machen wir das doch 'mal.

    Kopiere selbst die untere Funktion in eine Datei und speichere die Datei mit dem Namen vielftab.py.

    def print_tabelle(multiplikator):
        print "--- Drucke die %d -fach Tabelle ---" % multiplikator
        for n in range(1,13):
            print "%d x %d = %d" % (n, multiplikator, n*multiplikator)
    

    Gib jetzt ins Python-Prompt ein:

    >>> import vielftab
    >>> vielftab.print_tabelle(12)
    

    Booah eehh ! Du hast ein Modul erzeugt und es benutzt.

    Wichtige Anmerkung: Wenn du Python nicht vom gleichen Verzeichnis aus startest, wie demjenigen, in dem du die vielftab.py-Datei gespeichert hast, so wird Python die Datei möglicherweise nicht finden und einen Fehler melden. Falls das zutrifft, so hast du eine Umgebungsvariable zu erzeugen, PYTHONPATH genannt, die eine Liste von allen gültigen Verzeichnisse zur Modulsuche enthält (zusätzlich zu den von Python unterstützten Standardmodulen).

    Die Erzeugung von Umgebungsvariablen ist eine plattformspezifische Operation, wobei ich annehme, dass du entweder weisst wie man sie durchführt oder es sonst woher herausfindest.

     

    Module in BASIC und Tcl

    Was ist mit BASIC? Das ist komplexer.... In QBASIC und anderen älteren Erscheinungen besteht kein eigentliches Modul-Konzept. Du musst manuell von anderen Projekten benötigte Sachen kopieren und mit Hilfe deines Texteditors in dein aktuelles Projekt einfügen. Dennoch gibt es in Visual Basic ein Modulkonzept und du kannst ein Modul über das Integrated Development Environment (IDE) File|Open Module...-Menu laden. Dort gibt es einige Einschränkungen, welche Art von Dinge in einem BASIC-Module sein dürfen; da wir aber in diesem Kurs kein Visual Basic verwenden, würde das zu weit führen. (Anmerkung: Es gibt eine abgespeckte Version von Visual Basic, bekannt als die COM Controls Edition, CCE, frei runter zu laden bei Microsoft's website, falls du Lust zum Experimentieren hast. Auch Windows 98, 2000 und IE5 installieren alle eine verkürzte Version von VB, genannt VBScript, die man in Dateien mit der Endung .vbs verwenden kann.)

    Endlich Tcl, wie immer(!), ist wieder irgendwie eklektisch, aber nichtsdestotrotz interessant, und führt uns zur Betrachtung von wiederverwertbaren Modulen (oder wie diese dort bevorzugt genannt werden, libraries - Bibliotheken).

    Auf der einfachsten Stufe kannst du eine Datei mit Tcl-Funktionen erzeugen, wie wir es in Python getan haben und dann in deinem Programm diese Datei als source (Quelle) angeben. Dies veranlasst direkt den Interpreter deine Datei zu lesen und diese Programme werden für den Gebrauch zugänglich. Aber es gibt noch eine interessantere Option:

    Du kannst deine Dateien wie oben erzeugen, steckst sie dann alle in ein Verzeichnis und lässt dann ein mk_index -Kommando ablaufen. Dieses bildet einen Index von allen Funktionen und Files im Verzeichnis. Dann rufst du in deinem Programm ganz einfach die erforderliche Funktion auf und der TCL Interpreter wird erkennen, dass die Funktion nicht zugänglich ist und schaut automatisch im Indexfile. Es wird dann die relevanten Source-Files aus der Bibliothek "sourcen" und die Funktion ausführen.

    Eine einmal als Quelle definierte Funktion bleibt zugänglich, so dass diese bei allen weiteren Programmausführungen mitgeschleift wird. Der einzige Haken ist, dass der Programmierer mehr als eine Funktion mit dem gleichen Namen vermeiden muß. Diese Eigenschaft von Tcl ist bekannt als autoloading.

    Beim Folgenden werden wir einen Blick auf das Handling von Dateien und Texten werfen und dann, wie schon angekündigt, das Zählen von Wörtern in einer Datei vornehmen. Letztendlich werden wir ein Modul mit Funktionen zur Behandlung von Texten rein zu unserem Vergnügen erzeugen.

     

    Zur Erinnerung
  • Funktionen sind eine Art von Modul
  • Funktionen geben Werte zurück, Prozeduren nicht
  • Python-Module bestehen normalerweise aus Funktionsdefinitionen in einer Datei
  • Erzeugung neuer Funktionen mit dem def-Schlüsselwort in Python
  • Verwende SUB oder FUN in BASIC und proc in Tcl
  •  
    zurück  Inhalt  weiter 


    Im Falle von Fragen oder Hinweisen sende eine Nachricht in Englisch an den Autor: alan.gauld@yahoo.co.uk oder in Deutsch an den Übersetzer bup.schaefer@freenet.de