Module und Pakete

Module

Module bestehen, ebenso wie Python-Skript, aus Quellcode-Dateien mit der Endung .py. Prinzipiell kann somit jedes Python-Skript als eigenständiges Python-Modul angesehen werden, es existieren allerdings auch Module, die nur nur aus Hilfsfunktionen bestehen.

In der Praxis werden Module allerdings in erster Linie verwendet, um den Quellcode eines umfangreicheren Software-Projekts leichter zu organisieren und übersichtlich zu halten. Eine grobe Regel besagt, dass ein Modul nur so umfangreich sein sollte, dass sich seine Inhalte und sein Verwendungszweck mit ein bis zwei Sätzen zusammenfassen lassen sollten.

Jedes Modul stellt einen eigenen Namensraum für Variablen dar, so dass gleichnamige Funktionen, die in unterschiedlichen Modulen definiert sind, keine Konflikte verursachen.

Modulnamen werden in Python allgemein in Kleinbuchstaben geschrieben. Um in das aktuelle Programm ein externes Modul, also eine andere .py-Datei, zu integrieren, ist folgende Syntax üblich:

import modulname

# Beispiel:

import antigravity
import this

Das zu importierende Modul muss sich hierbei im Python-Pfad befinden, also entweder global installiert oder im aktuellen Verzeichnis enthalten sein. Andernfalls ist eine Anpassung der Variablen sys.path nötig.

Verwendung von Modulen

Nach dem Importieren eines Moduls können die im Modul definierten Klassen, Funktionen und Variablen mit folgender Syntax aufgerufen werden:

modulname.Klassenname
modulname.variablenname
modulname.funktionsname()

Um bei längeren Modulnamen Schreibarbeit sparen zu können, ist beim Importieren eines Moduls auch eine abkürzende Syntax möglich:

import modulname as myname

# Beispiel:

import math as m

Anschließend kann das Kürzel als Synonym für den Modulnamen verwendet werden, beispielsweise m.pi anstelle von math.pi.

Eine weitere Vereinfachung ist möglich, wenn man nur einzelne Klassen oder Funktionen eines Moduls importieren möchte. Hierfür ist folgende Syntax möglich:

from modulname import Klassenname   # oder
from modulname import funktionsname

Dabei können auch mehrere Klassen- oder Funktionsnamen jeweils durch ein Komma getrennt angegeben werden. Die so importieren Klassen bzw. Funktionen können dann direkt aufgerufen werden, als wären sie in der aktuellen Datei definiert.

Es ist auch möglich, mittels from modulname import * alle Klassen und Funktionen eines Moduls zu importieren. Hiervon ist allerdings (zumindest in Skript-Dateien) dringend abzuraten, da es dadurch unter Umständen schwer nachvollziehbar ist, aus welchem Modul eine später benutzte Funktion stammt. Zudem können Namenskonflikte entstehen, wenn mehrere Module gleichnamige Funktionen bereitstellen.

Hilfe zu Modulen

Mittels help(modulname) kann wiederum eine Hilfeseite zu dem jeweiligen Modul eingeblendet werden, in der üblicherweise eine Beschreibung des Moduls angezeigt wird und eine Auflistung der im Modul definierten Funktionen. Bei den Standard-Modulen wird zudem ein Link zur entsprechenden Seite der offiziellen Python-Dokumentation https://docs.python.org/3/ angegeben.

Die __name__-Variable

Jedes Modul bekommt, wenn es importiert wird, automatisch eine __name__-Variable zugewiesen, die den Namen des Moduls angibt. Wird allerdings eine Python-Datei unmittelbar als Skript mit dem Interpreter aufgerufen, so bekommt dieses “Modul” als __name__-Variable den Wert __main__ zugewiesen.

Wenn Python-Module importiert werden, dann werden sie einmalig vom Interpreter ausgeführt, das heißt alle darin aufgelisteten Definitionen und Funktionsaufrufe werden zum Zeitpunkt des Importierens (einmalig) aufgerufen. Möchte man einige Funktionen in einer Python-Datei nur dann ausführen, wenn die Datei als Skript aufgerufen wird, nicht jedoch, wenn sie als Modul in ein anderes Programm eingebunden wird, so kann man dies mittels folgender Anweisung erreichen:

if __name__ == __main__:

    # execute  this  only  if  the  current  file  is  interpreted  directly

Dies ist insbesondere für Mini-Programme nützlich, die wahlweise als selbstständiges Programm aufgerufen, oder in ein anderes Programm eingebettet werden können.

Module erneut laden

Ist ein Modul einmal importiert, so wird jede weitere import-Anweisung des gleichen Moduls vom Python-Interpreter ignoriert. Dies ist in den meisten Fällen von Vorteil, denn auch wenn beispielsweise mehrere Module eines Programms das Modul sys importieren, so wird dieses nur einmal geladen.

Schreibt man allerdings selbst aktiv an einem Programmteil und möchte die Änderungen in einer laufenden Interpreter-Sitzung (z.B. Ipython) übernehmen, so müsste der Interpreter nach jeder Änderung geschlossen und neu gestartet werden. Abhilfe schafft in diesem Fall die im Modul imp definierte Funktion reload(), die ein erneutes Laden eines Moduls ermöglicht:

import imp
import modulname

# Modul neu laden:
imp.reload(modulname)

Dies funktioniert auch, wenn ein Modul mit einer Abkürzung importiert wurde, beispielsweise import modulname as m; in diesem Fall kann das Modul mittels imp.reload(m) neu geladen werden.

Pakete

Mehrere zusammengehörende Module können in Python weiter in so genannten Paketen zusammengefasst werden. Ein Paket besteht dabei aus einem einzelnen Ordner, der mehrere Module (.py-Dateien) enthält.

Ein Programm kann somit in mehrere Teilpakete untergliedert werden, die wiederum mittels der import-Anweisung wie Module geladen werden können. Enthält beispielsweise ein Paket pac die Module a und b, so können diese mittels import pac geladen und mittels pac.a beziehungsweise pac.b benutzt werden; zur Trennung des Paket- und des Modulnamens wird also wiederum ein Punkt verwendet. Ebenso kann mittels import pac.a nur das Modul a aus dem Paket pac geladen werden.

Die import-Syntax für Pakete lautet somit allgemein:

# Alle Module eines Pakets importieren:
import paket

# Oder: Einzelne Module eines Pakets importieren:
import paket.modulname

# Alternativ:
from paket import modulname

Wird ein Modul mittels from paket import modulname importiert, so kann es ohne Angabe des Paketnamens benutzt werden; beispielsweise können darin definierte Funktionen mittels modulname.funktionsname() aufgerufen werden. Eine weitere Verschachtelung von Paketen in Unterpakete ist ebenfalls möglich.

Relative und absolute Pfadangaben

Um Module aus der gleichen Verzeichnisebene zu importieren, wird in Python folgende Syntax verwendet:

# Modul aus dem gleichen Verzeichnis importieren
from . import modulname

Mit . wird dabei der aktuelle Ordner bezeichnet. Ebenso kann ein Modul mittels from .. import modulname aus dem übergeordneten Verzeichnis und mittels from ... import modulname aus dem nochmals übergeordneten Verzeichnis importiert werden. Dies ist allerdings nicht in der Hauptdatei möglich, mit welcher der Python-Interpreter aufgerufen wurde und die intern lediglich den Namen __main__ zugewiesen bekommt: Diese darf nur absolute Pfadangaben für Imports enthalten.

Empfehlungen für Paket-Strukturen

Möchte man selbst ein Paket zu einer Python-Anwendung erstellen, so ist etwa folgender Aufbau empfehlenswert: [1]

My_Project/
|
|- docs
|   |- conf.py
|   |- index.rst
|   |- installation.rst
|   |- modules.rst
|   |- quickstart.rst
|   |- reference.rst
|
|- my_project/
|   |- main.py
|   |- ...
|
|- tests/
|   |- test_main.py
|   |- ...
|
|- CHANGES.rst
|- LICENSE
|- README.rst
|- requirements.txt
|- setup.py
|- TODO.rst

Das Projekt-Verzeichnis sollte den gleichen Namen wie die spätere Anwendung haben, allerdings mit einem Großbuchstaben beginnen, um Verwechslungen mit dem gleichnamigen Paket-Verzeichnis zu vermeiden. Das Projekt-Verzeichnis sollte neben dem eigentlichen Projekt-Paket zumindest noch die Ordner docs, und test umfassen, in denen eine Dokumentation des Projekts und zum Programm passende Tests enthalten sind; zusätzlich kann das Projekt einen lib-Ordner mit möglichen C-Erweiterungen enthalten. In manchen Projekten werden zudem ausführbare Programm-Dateien (meist ohne die Endung .py) in einem weiteren Ordner bin abgelegt; hat ein Programm allerdings nur eine einzelne aufrufbare Datei, so kann diese auch im Projekt-Hauptverzeichnis abgelegt werden.

Die mit Großbuchstaben benannten Dateien sind selbsterklärend, die Dateien requirements.txt und setup.py sind für die Installationsroutine vorgesehen.

Paketinstallation mit setuptools und setup.py

Das Paket setuptools aus der Python-Standardbibliothek stellt einige Funktionen bereit, die für das Installieren von Python-Paketen hilfreich sind. Dazu wird üblicherweise im Projekt eine Datei setup.py angelegt, die unter anderem eine Programmbeschreibung, den Namen und die Emailadresse des Autors, Informationen zur Programmversion und zu benötigten Fremdpaketen enthält. Zudem können mit der Funktion find_packages() aus dem setuptools-Paket die im Projektverzeichnis enthaltenen Pakete automatisch gefunden und bei Bedarf installiert werden.

Python-Entwickler haben das setuptools-Paket oftmals bereits installiert (aptitude install python3-setuptools), da es für das zusätzliche Installieren von weiteren Paketen mittels pip3 install paketname oder easy_install3 paketname hilfreich ist. Soll allerdings sichergestellt sein, dass auch bei Anwendern das setuptools-Paket installiert ist oder bei Bedarf nachinstalliert wird, kann die Datei ez_install.py von der Projektseite heruntergeladen und in das eigene Projektverzeichnis kopiert werden. In der Datei setup.py sind dann üblicherweise folgende Routinen vorhanden:

try:
    from setuptools import setup, find_packages
except ImportError:
    import ez_setup
    ez_setup.use_setuptools()
    from setuptools import setup, find_packages

import os

import my_project

my_project_path = os.path.abspath(os.path.dirname(__file__))

long_description =
"""
Eine ausführliche Beschreibung des Programms.
"""

setup(
    name='my_project',
    version=my_project.__version__,
    url='http://github.com/my_account/my_project/',
    license='GPLv3',
    author='Vorname Nachname',
    author_email='name@adresse.de',
    install_requires=[
        'Flask>=0.10.1',
        'SQLAlchemy==0.8.2',
        ],
    tests_require=['nose'],
    packages=find_packages(exclude=['tests']),
    description='Eine kurze Beschreibung.',
    long_description = long_description,
    platforms='any',
    keywords = "different tags here",
    classifiers = [
        'Programming Language :: Python',
        'Development Status :: 4 - Beta',
        'Natural Language :: English',
        'Intended Audience :: Developers',
        'Operating System :: Linux',
        'Topic :: Software Development :: Libraries :: Application Frameworks',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        ],

    )

Anmerkungen:

[1]Siehe auch Open Sourcing a Python Project the Right Way und Filesystem structure of a Python project für weitere Tips.