Import external libraries and organize your code into functional chunks. For interactive reading and executing code blocks and find b05-pypckg.ipynb, or install Python and JupyterLab locally.
Watch this section as a video
Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.
Einfuhrpakete oder -module¶
Durch das Importieren eines Moduls oder Pakets in Python werden die von diesem Modul oder Paket definierten oder freigegebenen Namen in einem Skript zugänglich gemacht. Diese können Funktionen, Klassen, Variablen und andere Objekte umfassen. Importierbarer Code kann aus verschiedenen Speicherorten und Formaten stammen: Er kann Teil von Python selbst sein, zur Standardbibliothek gehören, sich in einem lokalen Projekt befinden oder von einem installierten Drittanbieter-Paket bereitgestellt werden. Pakete von Drittanbietern werden üblicherweise mit Paketverwaltungstools wie conda oder pip (read more pip-installing) in die Interpreter-Umgebung installiert.
Module aus der Python-Standardbibliothek (z.B. os) und eingebaute Module (z.B. sys) sind normalerweise verfügbar, ohne zusätzliche Pakete zu installieren. Andere Module und Pakete müssen möglicherweise zuerst installiert werden.
The os module provides functions for interacting with the operating system, for example, for working with files, directories, paths, and environment variables. So let’s import this essential module:
import os
print(os.getcwd()) # print current working directory
print(os.path.abspath('')) # print directory of script running/home/schwindt/github/hyhome-v2/jupyter
/home/schwindt/github/hyhome-v2/jupyter
Überblick über Importoptionen¶
Hier ist eine Übersicht über Optionen zum Importieren von Paketen oder Modulen (hierarchische Teile von Paketen):
| Befehl | Beschreibung | Verwendung von Attributen |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| import package_name | Importieren Sie ein Originalmodul | package.item() |
| import package_name as nickname | Modul importieren und (alias) im Skript umbenennen | nickname.item() |
| from package-name import item | Importieren Sie nur eine Funktion, Klasse oder andere Elemente | item() |
| from package-name import * | Importieren Sie alle Artikel | item()
Beispiel¶
import matplotlib.pyplot as plt # import the pyplot module of the matplotlib package and alias it with plt
x = []
y = []
for e in range(1, 10):
x.append(e)
y.append(e**2)
plt.plot(x, y)Was ist der beste Weg, um ein Paket oder Modul zu importieren?¶
There is no global answer to this question. However, be aware that from package-name import * shadows any existing variable or other items in the script. Thus, only use * when you are aware of all contents of a module or package. This is also why PEP 8 discourages wildcard imports and recommends placing all imports at the top of a script. The import statement in the middle of the following example is for demonstration purposes only:
pi = 9.112 # define a float called pi
print(f"Pi is not {pi:.3f}.")
from math import pi # this overwrites the previously defined variable pi
print(f"Pi is {pi:.3f}.")Welche Elemente (Attribute, Klassen, Funktionen) befinden sich in einem Modul?¶
Sometimes we want to explore modules or check variable attributes. This is achieved with the dir() command:
import sys
print(sys.path)
print(dir(sys))
a_string = "zabaglione"
print(", ".join(dir(a_string)))['/home/schwindt/miniforge3/envs/wrr-proj/lib/python311.zip', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11/lib-dynload', '', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11/site-packages']
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
__add__, __class__, __contains__, __delattr__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getitem__, __getnewargs__, __getstate__, __gt__, __hash__, __init__, __init_subclass__, __iter__, __le__, __len__, __lt__, __mod__, __mul__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __rmod__, __rmul__, __setattr__, __sizeof__, __str__, __subclasshook__, capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill
Erstellen eines neuen Moduls¶
In der objektorientierten Programmierung und Codefaktorisierung ist das Schreiben von benutzerdefinierten, neuen Modulen eine wesentliche Aufgabe. Um ein neues Modul zu schreiben, erstellen Sie zuerst ein neues Skript. Öffnen Sie dann das neue Skript und fügen Sie einige Parameter und Funktionen hinzu.
# icecreamdialogue.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"icecreamdialogue.py can now either be executed as a script (nothing will happen visibly) or imported as a module to access its variables (e.g., icecreamdialogue.flavors):
import icecreamdialogue as icd
print(icd.welcome_msg)
scoops_wanted = 2
print(f"That makes {icd.price_scoops[scoops_wanted]} please")Machen Sie Script Standalone¶
As an alternative, we can append the call to items in icecreamdialogue.py in the script and run it as a stand-alone script by adding an if __name__ == "__main__": block:
# icecreamdialogue_standalone.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"
if __name__ == "__main__":
print(welcome_msg)
scoops_wanted = 2
print(f"That makes {price_scoops[scoops_wanted]} please")Now we can run icecreamdialogue_standalone.py in a terminal (e.g., Linux Terminal, PyCharm’s Terminal tab at the bottom of the window, or VS Code’s integrated terminal).
C:\temp\ python icecreamdialogue_standalone.pyStandalone-Scripts mit Eingabeparametern¶
Um das Skript flexibler zu gestalten, können wir beispielsweise scoops_wanted als Eingangsvariable einer Funktion definieren.
# icecreamdialogue_standalone_withinput.py
import sys # sys provides access to command line arguments
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"
def dialogue(scoops_wanted): # formerly in the __main__ statement
print(welcome_msg)
print(f"That makes {price_scoops[scoops_wanted]} please")
if __name__ == "__main__":
if len(sys.argv) > 1: # make sure input is provided
# if true: call the dialogue function with the input argument
dialogue(int(sys.argv[1]))Now, we can run icecreamdialogue_standalone_withinput.py in a terminal.
C:\temp\ python icecreamdialogue_standalone_withinput.py 2Initialisierung eines Pakets (Hierarchisch organisiertes Modul)¶
Good practice involves module cohesion and maintainability. Consequently, a package will most likely consist of multiple scripts that are stored in one folder and one core script serves for the initiation of the scripts. This core script is called __init__.py and Python will look for this script name in a package folder. Namespace packages may omit __init__.py. Example structure of a package called icecreamery:
icecreamery(Ordnername)__init__.py- optionale Paketinitiierung * Python * Skripticecreamdialogue.py- Dialog produziert * Python * Skripticecream_maker.py- virtuelles Eis, das * Python * Skript produziert
Um die beiden relevanten Skripte (Untermodule) des icecreamery-Pakets automatisch aufzurufen, muss das __init__.py Folgendes enthalten:
# __init__.py
print(f'Invoking __init__.py for {__name__}') # only for demonstration - keep __init__.py silent in production packages
import icecreamery.icecreamdialogue, icecreamery.icecream_maker# example usage of the icecreamery package
import icecreamery
print(icecreamery.icecreamdialogue.welcome_msg)Do you remember the dir() function? Applied to a package (e.g., dir(icecreamery)), it lists the items that are currently defined in the package namespace. However, to control which sub-modules a wildcard import (from icecreamery import *) loads, define an __all__ list in the __init__.py:
# __init__.py with __all__ list
__all__ = ['icecreamdialogue', 'icecream_maker']Das vollständige Beispiel des icecreamery_all Pakets ist auch in einem icecream Repository] verfügbar.
# example usage of the icecreamery package
from icecreamery_all import *
print(icecreamdialogue.welcome_msg)Zusammenfassung der Paketerstellung¶
The structure of a module can be more complex than the above example list (e.g., with sub-folders). When you write a package, consider using meaningful script and variable names, along with appropriate documentation.
Reload (Reimport) eines Pakets oder Moduls¶
Seit Python 3 muss beim Neuladen eines Moduls zuerst das importlib-Modul importiert werden. Das Nachladen ist nur sinnvoll, wenn Sie aktiv ein neues Modul schreiben. Um ein Modul neu zu laden, Typ:
import importlib
importlib.reload(my_module)Paketentwicklung & PyPI (Pip) Deployment¶
Das Beispiel icecreamery zeigt, wie ein Paket intern funktioniert. Um ein Paket über pip install icecreamery für jedermann installierbar zu machen, muss es in PyPI, dem Python Package Index, der pip im Hintergrund abfragt (recall pip-installing)] bereitgestellt werden. In diesem Abschnitt wird zunächst der Bereitstellungsworkflow, einschließlich der Automatisierung mit GitHub-Workflows und der Dokumentation zu Read the Docs, zusammengefasst und anschließend die bewährte Vorgehensweise für die gemeinsame Entwicklung eines Pakets erläutert.
Vom lokalen Code zu einem Pip-installierbaren Paket¶
Modern Python packaging is driven by a single pyproject.toml file, which replaces the formerly used setup.py (see PEP 621). A deployment-ready repository resembles the following structure, which is known as the src layout:
icecreamery/ (repository root)
src/
icecreamery/ (the package itself)
__init__.py
icecreamdialogue.py
icecream_maker.py
tests/ (automated tests, e.g., for pytest)
docs/ (documentation source, e.g., for Sphinx)
examples/ (functional usage examples)
pyproject.toml (package metadata and build configuration)
README.md
LICENSEDie pyproject.toml-Datei definiert, wie pip (oder ein anderes Installationsprogramm) das Paket erstellt und installiert:
[build-system]
requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta"
[project]
name = "icecreamery"
version = "0.1.0"
description = "Virtual ice cream sales dialogues"
readme = "README.md"
license = "BSD-3-Clause"
requires-python = ">=3.10"
dependencies = [
"matplotlib",
"numpy",
]Installieren Sie das Paket während der Entwicklung im editierbaren Modus in der aktiven Umgebung:
pip install -e .Mit dem Flag -e (editierbar) importiert Python das Paket direkt aus dem lokalen Entwicklungsklon anstelle einer statischen Kopie im Ordner site-packages, so dass Codeänderungen sofort ohne Neuinstallation wirksam werden.
Um ein Release auf PyPI manuell bereitzustellen:
Registrieren Sie sich (kostenlos) unter pypi.org und, um Uploads zu proben, unter test.pypi.org].
Build the distribution archives (a source archive and a wheel) with
python -m build(install the builder once withpip install build). The archives land in a newdist/folder.Laden Sie die Archive mit twine:
python -m twine upload dist/*] hoch. Best Practice: Proben Sie den Upload zuerst mitpython -m twine upload --repository testpypi dist/*.
Geschehen. Von nun an kann jeder pip install icecreamery.
Automatisieren von Testing und Deployment mit GitHub Workflows¶
Das manuelle Erstellen und Hochladen jedes Releases ist fehleranfällig. GitHub Actions automatisieren solche wiederkehrenden Jobs mit sogenannten Workflows, das sind YAML-Dateien, die im .github/workflows/-Ordner eines Repositorys gespeichert sind.] Zwei Workflows sind für die Paketentwicklung besonders nützlich:
A test (continuous integration) workflow that runs the test suite (e.g., with pytest) for every push and pull request, ideally on multiple Python versions and operating systems. Thus, broken code is flagged before it is merged into
main(recall the Collaboration & Branches section).Ein Publish-Workflow, der das Paket erstellt und auf PyPI hochlädt, wenn ein neues Release (Versions-Tag) auf GitHub veröffentlicht wird.
Best Practice für den Veröffentlichungs-Workflow ist PyPI Trusted Publishing, das das GitHub-Repository direkt mit dem PyPI-Projekt verknüpft (eine einmalige Einrichtung in den PyPI-Kontoeinstellungen), so dass keine API-Token in den Repository-Geheimnissen gespeichert werden müssen:
# .github/workflows/publish.yml
name: Publish to PyPI
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # required for PyPI Trusted Publishing
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build distribution archives
run: |
python -m pip install build
python -m build
- name: Upload to PyPI
uses: pypa/gh-action-pypi-publish@release/v1With this workflow in place, publishing a new version reduces to increasing the version number in pyproject.toml and clicking on Draft a new release (with a tag such as v0.1.1) on GitHub.
Dokumentation zu Read the Docs (Free Plan)¶
Ein Pip-installierbares Paket ohne Dokumentation wird kaum von irgendjemandem verwendet. Der De-facto-Standard für das Hosting von Python-Paketdokumentation ist Lesen Sie den Docs, der die Dokumentation öffentlicher (Open-Source-) Repositorien kostenlos unter PACKAGE-NAME.readthedocs.io erstellt und hostet (der kostenlose Plan zeigt kleine Anzeigen). Der Workflow:
Schreibe konsistente Docstrings (z.B. in numpy oder Googlestyle]) für alle Module, Klassen und Funktionen, damit Dokumentationsgeneratoren die API-Referenz automatisch wiedergeben können.
Create a
docs/folder with a Sphinx project (sphinx-quickstart) and enable thesphinx.ext.autodocandsphinx.ext.napoleonextensions indocs/conf.pyto pull the docstrings into the documentation. MkDocs with the mkdocstrings plugin is a popular alternative.Add a
.readthedocs.yamlconfiguration file (required by Read the Docs) to the repository root:
# .readthedocs.yaml
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.12"
sphinx:
configuration: docs/conf.py
python:
install:
- method: pip
path: .
- requirements: docs/requirements.txtSign in at readthedocs.org with a GitHub account and import the repository. Read the Docs installs a webhook, so every push to
maintriggers an automatic rebuild of the documentation, and every release tag can be published as a version-specific documentation build.
Geschehen. Die Dokumentation aktualisiert sich nun mit jedem Push.
Gemeinsame Paketentwicklung¶
As soon as several developers (e.g., a research group) push code to the same package repository, working on dedicated branches with pull requests is only half of the story. The following good practice rules keep a growing package maintainable (this list stems from painful experience with real-world research code):
** Streng folgen PEP 8:**
Namenskonventionen: Stellen Sie sicher, dass alle Skript (Modul) Dateinamen und Variablennamen der guten Praxis folgen, dh kurz,
lowercase_with_underscoresNamen für Module, Funktionen und Variablen,CamelCasefür Klassen undUPPERCASEfür Konstanten (erinnern Sie sich an meaningful script and variable names).Module shadowing: never name an internal script or folder after an installed library or module (e.g.,
math.py,numpy.py, or a folder calledmatplotlib/). Because Python searches the script’s own directory before the site-packages folder, the local file gets imported instead of the intended library, which leads to seemingly inexplicable import failures. Overly generic names, such asplots.pyorplots/, are risky for the same reason: they easily collide with third-party modules and get confused with plotting libraries likematplotlib.pyplot.Docstrings: rüsten Sie jedes Modul und jede Funktion mit einem Docstring aus, so dass Mitarbeiter (und Dokumentationsgeneratoren, siehe oben) verstehen, was der Code tut, ohne ihn zu reversieren.
Dateilänge und Coderedundanz: Eine modulare Paketstruktur bedeutet, dass Skripte prägnant bleiben sollten. Für den Kontext mussten wir einmal ein Plot-Skript umgestalten, das auf mehr als 3500 Zeilen angewachsen war, teilweise wegen kopierter (redundanter) Codeblöcke. Zerlegen Sie große Dateien in logische Submodule und folgen Sie strikt dem DRY-Prinzip (Don’t Repeat Yourself).
Verwenden Sie dedizierte Ordner für Beispiele und Vorlagen, um das Kernpaket (d.h. das src/-Verzeichnis) sauber zu halten:
dev-examples/: laufende Forschungs- oder Entwicklungsfälle (z. B.dev-examples/cylinder-flume-telemac/). Konfigurieren Sie das Repository so, dass es große Dateien blockiert (z. B. alles über 20 MB), da große Simulationsausgaben nicht in ein git-Repository gehören und separat gesichert werden sollten.examples/: finalisierte, bereinigte und funktionale Beispiele, zusammen mit README-Informationen darüber, wie man sie ausführt.templates/: verallgemeinerte Versionen der Beispielskripte, die Keyword-Argumente anstelle von Hardcode-Pfaden verwenden.
Maintain strict top-level cleanliness: do not add or move files directly into the repository root or the package source directory. For instance, keep environment activation scripts in a dedicated folder (e.g., env-scripts/) and invoke them from your local environment or example directories as needed.
Stay synchronized with main: pull the latest main branch regularly and always before creating a new branch. In addition, install the package from the local development clone in editable mode (pip install -e .) instead of manipulating relative imports or sys.path so that example scripts import your latest local code rather than a globally installed site-packages copy. AI assistants (e.g., Claude Code or Codex) can help to robustly refactor legacy scripts with broken imports, but review their modifications as critically as any other pull request.
Learning Success Check-up¶
Machen Sie den Lernerfolgstest für dieses Jupyter-Notebook].
Unfold QR Code
