Scripte in Python #!/usr/bin/env python

Der Einstieg in Python

Oft kommt man mit Python in Kontakt wenn es zum Beispiel darum geht, sich wiederholende Aufgaben programmatisch zu automatisieren. Ich hatte in meiner Studienzeit die Aufgabe, Protein-Sequenzen von einer öffentlichen Quelle herunterzuladen und diese in die Datenbank meines Instituts einzupflegen. Hierfür eignen sich natürlich auch andere Programmiersprachen, aber in diesem Fall habe ich ein bereits existierendes Python Script eines Kollegen bekommen, und musste hier nur wenige Anpassungen machen, um die Aufgabe zu erledigen.

Python macht das Scripting sehr einfach, gerade weil die Sprache selbst soweit selbst-erklärend geschrieben werden kann, dass man als Neuling relativ schnell versteht, worum es geht und was passiert.

Ein großer Vorteil von Python ist, dass geschriebene Scripte direkt ausführbar sind und daher Code Anpassungen direkt sichtbar werden. Der Weg zum Ziel ist hier also recht kurz, und das ist oft einer der Hauptgründe auf Python zurückzugreifen.

Aufbau eines Scripts

Ein Python Script, welches in einem Terminal direkt ausgeführt werden kann, besteht aus nur wenigen Komponenten:

  1. optionale shebang line
  2. optionale encoding line
  3. Variablen & Funktionen
  4. Funktionsaufruf(e)
  5. optionale „main“ Prüfung

Die shebang Line

Python Scripte enthalten in der ersten Zeile in der Regel eine für Neulinge eher kryptische Zeile, die meist so oder ähnlich aussieht:

#!/usr/bin/python  oder #!/usr/bin/env python

In Unix Systemen symbolisiert diese Zeile dem Betriebssystem,  mit welchem Interpreter die Datei ausgeführt werden kann. Ursprünglich stammt der Name shebang von der Interpreterzeile #!/bin/sh für Unix-Shell Scripte. Hier kann man den entsprechenden Wiki Eintrag dazu finden. Wir definieren hier also, dass das folgende Script mit dem Python Interpreter auszuführen ist. Wenn diese Zeile im Script enthalten ist, kann man das Script (wenn es entsprechende Berechtigung zur Ausführung hat) auch direkt ohne expliziter Angabe des Python Interpreters starten:

[~]$ ./a_script.py

Das Encoding

Ganz einfach: diese Zeile # -*- coding: utf-8 -*- besagt, dass die Datei im UTF-8 Format encodiert ist. Details zu solchen Encodings findet man hier

Variablen & Funktionen

Der shebang Zeile und dem hoffentlich vorhandenem Docstring für dieses Script schließen sich nun die Definition von Variablen und Funktionen an. Variablen können als einfache Zuweisung von Basis-Typen zu einem aussagekräftigen Variablen Namen definiert werden. Dabei kann der Typ beliebig gewählt werden und es muss keine Deklaration (eine Zuweisung zu einem spezifischen Typ z.B. „string“) der Variablen erfolgen z.B:

# a simple url as string
download_url = 'http://einfachpython.de/examples/scripte.txt'

Diese Variabilität macht Python natürlich sehr einsteigerfreundlich, kann aber bei komplexeren Programmen auch zu Problemem führen, wenn dem Entwickler unklar ist, welcher Typ in einer Variable hinterlegt ist und er eventuell ungültige Funktionen auf diesen Typ anwendet.

Funktionen werden mit dem Schlüsselwort ‚def‘ eingeleitet, gefolgt vom Funkionsnamen und den möglichen Parametern in einfachen Klammern umschlossen und beendet mit einem Doppelpunkt z.B:

def download_content(url):

Der dazugehörige Code der Funktion sowie Docstrings und Kommentare folgen dann in den sich anschließenden Zeilen und sind mit in der Regel vier Leerzeichen eingerückt.

Funktionsaufruf(e)

Der Python Interpreter beginnt beim Bearbeiten eines Scripts immer in Zeile 1 und liest zeilenweise alle auf dieser ersten Ebene eingerückten Variablen und Funktionsdefinitionen ein. Ausserdem kann hier bereits auch Code ausgeführt werden, wenn direkt auf der ersten Ebene z.B. for-Schleifen oder if-Bedingungen programmiert sind, oder aber Funktionen aufgerufen werden.

Wir können also zunächst unsere Variablen und Funktionen definieren und direkt im Anschluss ausführen. Die Ausführung gelingt allerdings nur, wenn die zur Ausführung nötigen Funktionen und Variablen dem Interpreter bereits bekannt sind. Sei es durch einen import aus einer anderen Python Datei oder eben durch die direkte vorgelagerte Definition.

Dennoch sollte man davon Abstand halten, ausführbaren Code direkt in ein Python Script zu integrieren. Warum das eine schlechte Idee ist, erklärt sich aus meinem letzten Satz im vorherigen Absatz: „import aus einer anderen Python Datei“

Wird in einem Python Script Code aus einer anderen Python Datei importiert, so ist das nichts anderes als eine Integration des Codes dieser Datei an der Stelle des import-Statements. Würde nun also Code in dieser zu importierenden Datei ausgeführt werden, so würde dies auch automatisch beim Import geschehen und kann daher ungewollte Nebeneffekte erzeugen. In der Regel möchte man durch einen Import nur Funktionen oder Klassen dieser Python Datei integrieren und bei konkretem Bedarf benutzen.

Um nun aber durch einen einfachen Start unseres Python Scriptes die gewünschte Funktion aufzurufen, wird eine Main Prüfund in des Script integriert:

Main Prüfung

if __name__ == "__main__":     
    download_content(url)

Die oben genannte if-Anweisung prüft, ob das durch den Python Interpreter gerade an dieser Stelle durchlaufene Script direkt, d.h. als „main“ Script aufgerufen wurde. Wenn der Python Interpreter durch einen Import an diese Stelle gelangt, so liefert dieses Statement „False“ und der sich anschließende eingerückte Code wird nicht ausgeführt. Ist das Script jedoch direkt aufgerufen worden, so liefert das Statement  „True“ und wir können anschließend unsere Funktion aufrufen.

Ein Beispiel:
a_script.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
a simple script with a main method, it imports another
module 'another_script' that is also a script
"""
__author__ = 'christian'
__created__ = '17.08.17'
 
import another_script
 
simple_variable = 123
 
 
def main():
    """
    this is a simple function
    """
    print("my name is a_script.py and I was run as a script")
    print(simple_variable)
 
 
if __name__ == "__main__":
    print("run a_script.py as a script...")
    main()  # calls the main function

another_script.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
this is another script and will not execute code unless directly started as a script
"""
__author__ = 'christian'
__created__ = '17.08.17'
 
print("this line of another_script.py is hit by the interpreter during import")
 
 
def main():
    """
    this is another simple function
    """
    print("my name is another_script.py and I was run as a script")
 
 
if __name__ == "__main__":
    print("run another_script.py as a script...")
    main()  # calls the main function

und die Ausgabe in einer unix console:

christian@c-s-media:[~/example]$ ./a_script.py
this line of another_script.py is hit by the interpreter during import
run a_script.py as a script...
my name is a_script.py and I was run as a script
123
christian@c-s-media:[~/example]$ python another_script.py
this line of another_script.py is hit by the interpreter during import
run another_script.py as a script...
my name is another_script.py and I was run as a script

Hier findet man eine genaue Definition des sogenannten Top-Level-Script Environments