Anhang 2: Python Module, Packages und Imports verstehen
October 2025 (10352 Words, 58 Minutes)

1. Einführung: Das Import-System von Python entmystifizieren
Willkommen zu Anhang 2! Diese Vorlesung behandelt eine der häufigsten Verwirrungsquellen für Python-Entwickler: das Import-System. Wenn Sie jemals Fehler wie diese erlebt haben:
ImportError: attempted relative import with no known parent package
ModuleNotFoundError: No module named 'road_profile_viewer'
ImportError: cannot import name 'find_intersection' from 'geometry'
…dann ist diese Vorlesung genau richtig für Sie.
1.1 Warum das jetzt wichtig ist
Sie arbeiten gerade an der road-profile-viewer Refactoring-Aufgabe, bei der Sie ein monolithisches Python-Skript in ein modulares Projekt mit mehreren Dateien umwandeln:
road_profile_viewer/
├── geometry.py
├── road.py
├── visualization.py
└── main.py
Sobald Sie Code auf mehrere Dateien aufteilen, müssen Sie Funktionen aus einem Modul in ein anderes importieren. Und da entstehen die Fragen:
- Was ist genau ein “Modul” vs. ein “Package”?
- Brauche ich
__init__.py-Dateien? - Soll ich
from .visualization importoderfrom road_profile_viewer.visualization importschreiben? - Was bedeuten die Punkte? Was ist der Unterschied zwischen
.,..und...? - Warum kann ich einfach
import numpyschreiben, aber das funktioniert nicht mit meinem eigenen Code?
1.2 Was Sie lernen werden
Am Ende dieser Vorlesung werden Sie verstehen:
- ✅ Was Python-Module und Packages sind (und wie sie sich unterscheiden)
- ✅ Wann
__init__.pybenötigt wird (und wann es optional ist) - ✅ Absolute Imports: Der empfohlene Ansatz für die meisten Projekte
- ✅ Relative Imports: Wie die Punkt-Notation funktioniert (und wann man sie verwendet)
- ✅ Wie externe Packages wie
numpyanders funktionieren als Ihre eigenen Module - ✅ Häufige Import-Fehler und wie man sie behebt
- ✅ Best Practices für die Strukturierung von Python-Projekten
1.3 Voraussetzungen
Diese Vorlesung baut auf Konzepten auf aus:
- Chapter 01 (Modern Development Tools) Teil 2: Python Umgebungen,
uvundpyproject.toml - Chapter 02 (Refactoring): Refactoring vom Monolithen zu Modulen
Sie sollten das initiale Setup des road-profile-viewer Projekts abgeschlossen haben.
2. Was ist ein Python-Modul?
Beginnen wir mit den Grundlagen: Was ist ein Modul?
2.1 Die einfache Definition
Für praktische Zwecke können Sie sich ein Python-Modul als eine .py-Datei vorstellen, die Python-Code enthält.
Das ist das einfachste mentale Modell. Wenn Sie geometry.py erstellen, haben Sie ein Modul namens geometry erstellt.
Offizielle Definition:
Laut dem Python Glossar ist ein Modul “ein Objekt, das als organisatorische Einheit von Python-Code dient”. Technisch gesehen können Module aus verschiedenen Quellen stammen (.py-Dateien, C-Erweiterungen, eingebaute Module), aber für diesen Kurs konzentrieren wir uns auf .py-Dateien—den häufigsten Typ, mit dem Sie arbeiten werden.
Für ein umfassendes Verständnis siehe:
2.2 Die Module Ihres Projekts
In Ihrem road-profile-viewer Projekt haben Sie vier Module:
road_profile_viewer/
├── geometry.py ← Modul: geometry
├── road.py ← Modul: road
├── visualization.py ← Modul: visualization
└── main.py ← Modul: main
Jede Datei ist eine eigenständige Einheit von Python-Code, die:
- Funktionen, Klassen oder Variablen definiert
- In andere Python-Dateien importiert werden kann
- Ihren eigenen Namespace hat (Variablennamen kollidieren nicht zwischen Modulen)
2.3 Was ist in einem Modul?
Schauen wir uns eine vereinfachte Version Ihres geometry.py an:
# geometry.py
import numpy as np
from typing import Optional, Tuple
def calculate_ray_line(
x: float,
ray_angle: float,
line_start: Tuple[float, float],
line_end: Tuple[float, float]
) -> Optional[Tuple[float, float]]:
"""Berechnet den Schnittpunkt zwischen einem Strahl und einem Liniensegment."""
# Implementierung hier...
return intersection_point
def find_intersection(x: float, ray_angle: float, road_profile: list) -> Optional[float]:
"""Findet den ersten Schnittpunkt eines Strahls mit dem Straßenprofil."""
# Implementierung hier...
return distance
Dieses Modul stellt zwei Funktionen bereit, die andere Teile Ihres Codes importieren und verwenden können.
2.4 Module vs. Skripte
Hier ist ein wichtiger Unterschied:
Eine Python-Datei kann auf zwei Arten verwendet werden:
- Als Modul (von anderem Code importiert):
from geometry import calculate_ray_line - Als Skript (direkt ausgeführt):
python geometry.py
Die meisten Dateien in Ihrem Projekt sind Module, die importiert werden sollen, nicht direkt ausgeführt. Deshalb sehen Sie dieses Muster:
# main.py
if __name__ == "__main__":
# Dies läuft nur, wenn als Skript ausgeführt
main()
Dadurch kann main.py auf beide Arten funktionieren:
- ✅ Direkt ausführen:
python main.py - ✅ Woanders importieren:
from main import main
2.5 Wie Python Module findet
Wenn Sie import geometry schreiben, sucht Python nach geometry.py an diesen Orten in dieser Reihenfolge:
- Aktuelles Verzeichnis (wo Sie Python ausführen)
- Verzeichnisse in
PYTHONPATH(Umgebungsvariable) site-packages/der virtuellen Umgebung (installierte Packages)- Standardbibliothek (eingebaute Module wie
os,sys,json)
Sie können diese Liste sehen mit:
import sys
print(sys.path)
Ausgabe (Beispiel):
[
'/home/student/road-profile-viewer', # Aktuelles Verzeichnis
'/home/student/.venv/lib/python3.12/site-packages', # venv packages
'/usr/lib/python3.12', # Standardbibliothek
]
Deshalb funktioniert import numpy: numpy ist im site-packages/-Verzeichnis Ihrer virtuellen Umgebung installiert.
2.6 Das Problem mit direkter Ausführung
Probieren Sie dieses Experiment:
# Dies wird FEHLSCHLAGEN mit Import-Fehlern:
cd road_profile_viewer
python geometry.py
Fehler:
ModuleNotFoundError: No module named 'road_profile_viewer'
Warum? Weil Python nicht weiß, dass road_profile_viewer ein Package ist, wenn Sie python geometry.py ausführen. Es sieht nur geometry.py als eigenständiges Skript.
Die Lösung (wir erklären später warum):
# Das FUNKTIONIERT:
cd .. # Zum Projekt-Root gehen
python -m road_profile_viewer.geometry
Das -m Flag sagt Python, road_profile_viewer.geometry als Modul innerhalb eines Packages zu behandeln, nicht nur als Skript.
2.7 Wichtigste Erkenntnisse: Module
- ✅ Ein Modul ist einfach eine
.py-Datei - ✅ Jede Python-Datei ist ein Modul
- ✅ Module bieten einen Namespace für Funktionen, Klassen, Variablen
- ✅ Module können importiert oder als Skripte ausgeführt werden
- ✅ Python durchsucht
sys.path, um Module zu finden
3. Was ist ein Python-Package?
Jetzt gehen wir eine Stufe höher: Was ist ein Package?
3.1 Die einfache Definition
Ein Python-Package ist ein Verzeichnis, das Python-Module enthält.
Mit anderen Worten: Ein Package ist ein Ordner mit .py-Dateien, den Python als zusammenhängende Einheit behandelt.
3.2 Ihr Projekt ist ein Package
Ihr road_profile_viewer-Verzeichnis ist ein Python-Package:
road_profile_viewer/ ← Dies ist ein PACKAGE
├── __init__.py ← Optional (mehr dazu gleich)
├── geometry.py ← Modul im Package
├── road.py ← Modul im Package
├── visualization.py ← Modul im Package
└── main.py ← Modul im Package
Dieses Package enthält vier Module: geometry, road, visualization und main.
3.3 Packages ermöglichen hierarchische Organisation
Packages ermöglichen es Ihnen, Code in logische Gruppen zu organisieren:
my_project/
├── data/
│ ├── __init__.py
│ ├── loader.py
│ └── processor.py
├── models/
│ ├── __init__.py
│ ├── neural_net.py
│ └── decision_tree.py
└── utils/
├── __init__.py
├── math.py
└── plotting.py
Hier haben wir drei Packages (data, models, utils), jedes mit zugehörigen Modulen.
3.4 Die Rolle von __init__.py
Dies ist eine der häufigsten Verwirrungsquellen!
3.4.1 Python 3.3+ (Modernes Python)
In Python 3.3 und später ist __init__.py optional für einfache Packages. Python unterstützt “Namespace Packages”, die kein __init__.py benötigen.
Ihr Projekt funktioniert ohne __init__.py:
road_profile_viewer/
├── geometry.py
├── road.py
├── visualization.py
└── main.py
# Kein __init__.py nötig!
Sie können trotzdem Module importieren:
from road_profile_viewer.geometry import find_intersection # ✅ Funktioniert!
3.4.2 Wann __init__.py immer noch nützlich ist
Obwohl es optional ist, erfüllt __init__.py wichtige Zwecke:
1. Package-Initialisierungscode
Sie können Setup-Code ausführen, wenn das Package zum ersten Mal importiert wird:
# road_profile_viewer/__init__.py
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Road Profile Viewer Package geladen")
⚠️ Wichtige Warnung: Minimieren Sie Code in __init__.py
Obwohl es verlockend ist, hier Initialisierungscode hinzuzufügen, halten Sie __init__.py so minimal wie möglich:
- ❌ Fügen Sie keine Logging-Konfiguration in
__init__.pyhinzu für große Projekte - ❌ Führen Sie keine teuren Berechnungen zur Importzeit aus
- ❌ Importieren Sie keine schweren Abhängigkeiten unnötigerweise
Warum? Code in __init__.py wird jedes Mal ausgeführt, wenn das Package importiert wird, was:
- Ihre Tests verlangsamt (Imports passieren wiederholt in Test-Suites)
- CI/CD Build-Zeiten erhöht
- Ihr Package schwerer als Abhängigkeit verwendbar macht
- Zirkuläre Import-Probleme verursachen kann
Best Practice: Fügen Sie nur Code zu __init__.py hinzu, wenn es absolut notwendig ist, damit das Package funktioniert. Und selbst dann brauchen Sie es wahrscheinlich nicht so sehr, wie Sie denken! 😉
Besserer Ansatz für Logging:
# Anstatt Logging in __init__.py zu konfigurieren, tun Sie es in Ihrem Haupteinstiegspunkt:
# main.py
import logging
def main():
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Ihr Anwendungscode...
2. Bequeme Imports
Häufig verwendete Funktionen auf Package-Ebene verfügbar machen:
# road_profile_viewer/__init__.py
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
# Jetzt können Benutzer dies tun:
from road_profile_viewer import find_intersection # ✅ Kürzer!
# Anstatt:
from road_profile_viewer.geometry import find_intersection # ✅ Funktioniert auch
3. __all__ definieren
Kontrollieren, was from package import * importiert:
# road_profile_viewer/__init__.py
__all__ = ['find_intersection', 'generate_road_profile', 'create_dash_app']
⚠️ Wichtige Hinweise zu __all__ und import *
Während __all__ für Bibliotheksautoren nützlich ist, um eine öffentliche API zu definieren, gibt es wichtige Vorbehalte:
Über import * (Stern-Imports):
- ❌ Vermeiden Sie
from package import *in Ihrem Code - gilt allgemein als schlechte Praxis - ❌ Macht Code schwerer lesbar - unklar, woher Namen kommen
- ❌ Kann Namenskonflikte verursachen - überschreibt vorhandene Variablen
- ❌ Performance-Auswirkung - importiert alles, auch ungenutzten Code
- ✅ Ruff Linter wird Sie warnen über Stern-Imports (und Sie sollten darauf hören!)
Warum __all__ trotzdem wichtig ist:
- ✅ Dokumentiert die öffentliche API Ihres Packages (was Benutzer importieren sollten)
- ✅ Wird von Dokumentationstools wie Sphinx verwendet
- ✅ Hilft IDEs, bessere Autovervollständigung bereitzustellen
Best Practice:
# ❌ MACHEN SIE DAS NICHT:
from road_profile_viewer import * # Was wurde importiert? Wer weiß!
# ✅ MACHEN SIE STATTDESSEN:
from road_profile_viewer import find_intersection, generate_road_profile # Explizit und klar
Performance-Hinweis: In großen Projekten kann selbst die Definition von __all__ Auswirkungen haben. Wenn Ihr __init__.py viele Module importiert, nur um __all__ zu füllen, laden Sie Code, der möglicherweise nicht benötigt wird. Deshalb verwenden viele große Bibliotheken lazy imports oder halten __all__ minimal.
4. Package-Metadaten
Versionsinformationen und Package-Konstanten speichern:
# road_profile_viewer/__init__.py
__version__ = "1.0.0"
__author__ = "Ihr Name"
# Verwendung woanders:
import road_profile_viewer
print(road_profile_viewer.__version__)
3.4.3 Reguläre Packages vs. Namespace Packages
Reguläres Package (hat __init__.py):
my_package/
├── __init__.py ← Macht dies zu einem regulären Package
└── module.py
Namespace Package (kein __init__.py):
my_package/
└── module.py ← Funktioniert trotzdem in Python 3.3+
Wann welches verwenden:
- Reguläres Package: Wenn Sie Initialisierungscode, bequeme Imports oder explizite Package-Grenzen benötigen
- Namespace Package: Einfache Projekte, keine spezielle Initialisierung nötig
3.5 Hands-On: __init__.py zu Ihrem Projekt hinzufügen
Fügen wir ein minimales __init__.py zu Ihrem road-profile-viewer hinzu:
Erstellen Sie road_profile_viewer/__init__.py:
"""
Road Profile Viewer
Ein Python-Package zur Visualisierung von Straßenhöhenprofilen mit interaktivem Ray-Tracing.
"""
__version__ = "1.0.0"
# Bequeme Imports für häufige Anwendungsfälle
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
__all__ = [
'find_intersection',
'generate_road_profile',
'create_dash_app',
]
Vorteile:
# Vorher (Namespace Package):
from road_profile_viewer.geometry import find_intersection
# Nachher (Reguläres Package mit __init__.py):
from road_profile_viewer import find_intersection # ✅ Kürzer!
# Versionsinformationen:
import road_profile_viewer
print(road_profile_viewer.__version__) # ✅ "1.0.0"
3.6 Verschachtelte Packages
Packages können andere Packages enthalten:
road_profile_viewer/
├── __init__.py
├── main.py
├── core/
│ ├── __init__.py
│ ├── geometry.py
│ └── road.py
└── ui/
├── __init__.py
└── visualization.py
Jetzt haben Sie:
- Haupt-Package:
road_profile_viewer - Sub-Package:
road_profile_viewer.core - Sub-Package:
road_profile_viewer.ui
Import-Pfade spiegeln diese Struktur wider:
from road_profile_viewer.core.geometry import find_intersection
from road_profile_viewer.ui.visualization import create_dash_app
3.7 Wichtigste Erkenntnisse: Packages
- ✅ Ein Package ist ein Verzeichnis mit Modulen
- ✅
__init__.pyist optional in Python 3.3+, aber nützlich für Initialisierung und bequeme Imports - ✅ Packages ermöglichen hierarchische Code-Organisation
- ✅ Ihr
road_profile_viewer-Verzeichnis ist ein Package - ✅ Import-Pfade verwenden Punkte zur Navigation in der Package-Struktur:
package.subpackage.module
4. Absolute Imports: Der empfohlene Ansatz
Jetzt gehen wir den wichtigsten Import-Stil an: absolute Imports.
4.1 Was sind absolute Imports?
Absolute Imports verwenden den vollständigen Pfad vom Projekt-Root.
Syntax:
from package.module import function
4.2 Absolute Imports in Ihrem Projekt
In Ihrem road-profile-viewer sehen absolute Imports so aus:
In visualization.py:
# Import aus dem geometry-Modul
from road_profile_viewer.geometry import find_intersection
# Import aus dem road-Modul
from road_profile_viewer.road import generate_road_profile
In main.py:
# Import aus dem visualization-Modul
from road_profile_viewer.visualization import create_dash_app
4.3 Wie absolute Imports funktionieren
Wenn Python from road_profile_viewer.geometry import find_intersection sieht, macht es:
- Sucht nach
road_profile_viewerinsys.path - Sucht nach
geometry.pyin diesem Package - Importiert die Funktion
find_intersectionaus diesem Modul
Visuelle Darstellung:
sys.path enthält: /home/student/project/
↓
road_profile_viewer/ ← Gefunden!
↓
geometry.py ← Gefunden!
↓
def find_intersection ← Importiere dies!
4.4 Warum die Aufgabe absolute Imports erfordert
Ihre GitHub Classroom-Aufgabe besagt:
Absolute Imports sind verpflichtend, um “ImportError: attempted relative import with no known parent package” zu vermeiden.
Warum diese Anforderung? Schauen wir uns an, was mit verschiedenen Ausführungsbefehlen passiert:
Szenario 1: Als Skript ausführen (SCHEITERT mit relativen Imports)
cd road_profile_viewer
python main.py
Wenn main.py relative Imports wie from .visualization import create_dash_app verwendet, erhalten Sie:
ImportError: attempted relative import with no known parent package
Warum? Python weiß nicht, dass road_profile_viewer ein Package ist, wenn Sie python main.py direkt ausführen.
Szenario 2: Als Modul ausführen (FUNKTIONIERT)
cd .. # Projekt-Root
python -m road_profile_viewer.main
Jetzt weiß Python:
road_profile_viewerist ein Packagemainist ein Modul darin- Relative Imports funktionieren korrekt
Szenario 3: Absolute Imports verwenden (FUNKTIONIERT IMMER)
# Von überall:
python -m road_profile_viewer.main # ✅ Funktioniert
cd road_profile_viewer && python main.py # ✅ Funktioniert auch!
Absolute Imports funktionieren, weil sie nicht davon abhängen, dass Python die Package-Struktur kennt - sie verwenden sys.path.
4.5 Module ausführen: Das -m Flag
Best Practice: Verwenden Sie immer -m zum Ausführen von Modulen in Packages:
# ✅ GUT: Als Modul ausführen
python -m road_profile_viewer.main
# ❌ SCHLECHT: Als Skript ausführen (bricht relative Imports)
python road_profile_viewer/main.py
Das -m Flag sagt Python:
- Behandle dies als Modulpfad, nicht als Dateipfad
- Richte das Import-System korrekt ein
- Mache relative Imports funktionsfähig (wenn Sie sie verwenden)
4.6 Wie uv run damit umgeht
Wenn Sie uv run verwenden:
uv run python -m road_profile_viewer.main
uv macht automatisch:
- Aktiviert die virtuelle Umgebung
- Stellt sicher, dass
road_profile_viewerinsys.pathist - Läuft mit dem richtigen Python-Interpreter
4.7 Vollständiges Beispiel: Absolute Imports in Ihrem Projekt
Datei: road_profile_viewer/geometry.py
"""Geometrieberechnungen für Strahl-Linien-Schnittpunkte."""
import numpy as np
from typing import Optional, Tuple
def find_intersection(x: float, ray_angle: float, road_profile: list) -> Optional[float]:
"""Findet Schnittpunkt zwischen Strahl und Straßenprofil."""
# Implementierung...
return distance
Datei: road_profile_viewer/road.py
"""Straßenprofil-Generierung."""
import numpy as np
def generate_road_profile(num_points: int = 100) -> np.ndarray:
"""Generiert ein zufälliges Straßenhöhenprofil."""
# Implementierung...
return profile
Datei: road_profile_viewer/visualization.py
"""Dash-Webanwendung zur Visualisierung."""
import dash
from dash import dcc, html
import plotly.graph_objects as go
# ✅ Absolute Imports
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
def create_dash_app():
"""Erstellt und gibt eine Dash-Anwendung zurück."""
app = dash.Dash(__name__)
# Verwendung importierter Funktionen
profile = generate_road_profile()
distance = find_intersection(0, 45, profile)
# UI aufbauen...
return app
Datei: road_profile_viewer/main.py
"""Einstiegspunkt für die Road Profile Viewer-Anwendung."""
# ✅ Absoluter Import
from road_profile_viewer.visualization import create_dash_app
def main():
"""Führt die Anwendung aus."""
app = create_dash_app()
app.run_server(debug=True, host='127.0.0.1', port=8050)
if __name__ == "__main__":
main()
Die App ausführen:
# Vom Projekt-Root:
uv run python -m road_profile_viewer.main
4.8 Vorteile absoluter Imports
✅ Klarheit: Sofort offensichtlich, woher Code kommt ✅ Zuverlässigkeit: Funktioniert unabhängig davon, wie das Modul ausgeführt wird ✅ IDE-Unterstützung: Bessere Autovervollständigung und Navigation ✅ Refactoring-Sicherheit: Dateien verschieben bricht keine Imports (solange Package-Struktur gleich bleibt) ✅ Keine Mehrdeutigkeit: Kann nicht mit Standardbibliothek oder externen Packages verwechselt werden
4.9 Wichtigste Erkenntnisse: Absolute Imports
- ✅ Verwenden Sie vollständigen Pfad vom Package-Root:
from road_profile_viewer.module import function - ✅ Funktioniert unabhängig davon, wie Code ausgeführt wird
- ✅ Empfohlen für die meisten Projekte (einschließlich diesem Kurs)
- ✅ Führen Sie Module mit
python -m package.modulefür beste Ergebnisse aus - ✅ Ihre Aufgabe erfordert absolute Imports für Zuverlässigkeit
5. Relative Imports: Wann und wie
Jetzt erkunden wir relative Imports - den alternativen Ansatz mit Punkten.
5.1 Was sind relative Imports?
Relative Imports verwenden Punkte zur Navigation in der Package-Hierarchie.
Syntax:
from .module import function # Aktuelles Package
from ..module import function # Eltern-Package
from ...module import function # Großeltern-Package
5.2 Die Punkt-Notation erklärt
Denken Sie an Punkte wie Dateisystempfade:
.= aktuelles Verzeichnis (aktuelles Package)..= Elternverzeichnis (Eltern-Package)...= Großelternverzeichnis (Großeltern-Package)
5.3 Ihr Projekt mit relativen Imports
Datei: road_profile_viewer/visualization.py
# Relative Imports (Alternative zu absoluten)
from .geometry import find_intersection # ← Ein Punkt
from .road import generate_road_profile # ← Ein Punkt
Übersetzung:
.geometrybedeutet “dasgeometry-Modul im selben Package wie ich”.roadbedeutet “dasroad-Modul im selben Package wie ich”
Datei: road_profile_viewer/main.py
# Relativer Import
from .visualization import create_dash_app # ← Ein Punkt
5.4 Die Punkte verstehen: Visueller Leitfaden
Stellen Sie sich diese komplexere Struktur vor:
my_project/
├── package_a/
│ ├── __init__.py
│ ├── module1.py
│ └── sub_package/
│ ├── __init__.py
│ └── module2.py
└── package_b/
├── __init__.py
└── module3.py
Von package_a/sub_package/module2.py:
# Import aus dem gleichen Package (sub_package)
from . import module2 # ← Aktuelles Package (sub_package)
# Import aus dem Eltern-Package (package_a)
from .. import module1 # ← Eine Ebene nach oben
# Import aus Geschwistermodul im Eltern-Package
from ..module1 import function # ← Hoch dann in module1
# Import aus völlig anderem Package (FUNKTIONIERT NICHT)
from ...package_b import module3 # ❌ Kann nicht über Root hinaus!
Visuelle Darstellung:
module2.py Ort: package_a/sub_package/module2.py
from . → package_a/sub_package/ (aktuell)
from .. → package_a/ (eine nach oben)
from ... → my_project/ (zwei nach oben - root)
from .... → ❌ FEHLER: jenseits von root
5.5 Relativ vs. Absolut: Nebeneinander
Vergleichen wir die gleichen Imports in beiden Varianten:
Absolute Imports:
# visualization.py
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
Relative Imports:
# visualization.py
from .geometry import find_intersection
from .road import generate_road_profile
Beide funktionieren gleich! Die Wahl ist eine Frage des Stils und der Projektanforderungen.
5.6 Wann relative Imports brechen: Der berüchtigte Fehler
Versuchen Sie, Ihren Code mit relativen Imports auszuführen:
cd road_profile_viewer
python main.py
Fehler:
ImportError: attempted relative import with no known parent package
Warum passiert das?
Wenn Sie python main.py ausführen, behandelt Python main.py als eigenständiges Skript, nicht als Modul in einem Package. Relative Imports erfordern, dass Python weiß:
- Zu welchem Package das Modul gehört
- Wo sich das Eltern-Package befindet
Bei direkter Ausführung als Skript hat Python diese Informationen nicht!
Die Lösung:
# Als Modul ausführen, nicht als Skript:
cd .. # Zum Projekt-Root gehen
python -m road_profile_viewer.main # ✅ Funktioniert!
Jetzt weiß Python:
mainist ein Modul imroad_profile_viewerPackage- Relative Imports wie
from .visualizationkönnen aufgelöst werden
5.7 Wann relative Imports verwenden
Relative Imports sind nützlich, wenn:
✅ Große Packages mit tiefen Hierarchien ✅ Package wird umbenannt (relative Imports kodieren den Package-Namen nicht fest) ✅ Sub-Packages, die eigenständig sein sollen
Beispiel: Eine Bibliothek, die geforkt werden könnte:
# Wenn Ihr Package von "original_name" zu "forked_name" umbenannt werden könnte
# Relative Imports brechen nicht:
from .utils import helper # ✅ Funktioniert mit jedem Package-Namen
# Absolute Imports würden Updates benötigen:
from original_name.utils import helper # ❌ Bricht nach Umbenennung
5.8 Wann absolute Imports verwenden
Absolute Imports sind besser, wenn:
✅ Kleine bis mittelgroße Projekte (wie road-profile-viewer) ✅ Module als Skripte ausführen (keine Eltern-Package-Verwirrung) ✅ Team-Projekte (klarer, expliziter) ✅ Kurs-Aufgaben (für diesen Kurs erforderlich!)
5.9 Absolute und Relative mischen
Sie können beide Stile im selben Projekt mischen:
# visualization.py
from road_profile_viewer.geometry import find_intersection # Absolut
from .road import generate_road_profile # Relativ
import numpy as np # Externes Package
Konsistenz ist jedoch besser! Wählen Sie einen Stil und bleiben Sie dabei im gesamten Projekt.
5.10 Wichtigste Erkenntnisse: Relative Imports
- ✅ Verwenden Sie Punkte zur Navigation:
.(aktuell),..(Eltern),...(Großeltern) - ✅ Funktionieren nur, wenn Python weiß, dass das Modul in einem Package ist
- ✅ Brechen, wenn Module direkt als Skripte ausgeführt werden
- ✅ Müssen
python -m package.moduleverwenden, damit relative Imports funktionieren - ✅ Für diesen Kurs: verwenden Sie stattdessen absolute Imports
6. Absolut vs. Relativ: Der praktische Vergleich
Machen wir einen umfassenden Vergleich, um Ihnen zu helfen, informierte Entscheidungen zu treffen.
6.1 Szenario: Refactoring in Unterverzeichnisse
Stellen Sie sich vor, Sie reorganisieren Ihr Projekt in Unterverzeichnisse:
Aktuelle Struktur:
road_profile_viewer/
├── geometry.py
├── road.py
├── visualization.py
└── main.py
Neue Struktur:
road_profile_viewer/
├── core/
│ ├── geometry.py
│ └── road.py
├── ui/
│ └── visualization.py
└── main.py
6.2 Mit absoluten Imports
Vorher (flache Struktur):
# visualization.py
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
Nachher (verschachtelte Struktur) - ERFORDERT ÄNDERUNGEN:
# ui/visualization.py
from road_profile_viewer.core.geometry import find_intersection
from road_profile_viewer.core.road import generate_road_profile
Auswirkung: ❌ Muss Import-Pfade in allen Dateien aktualisieren
6.3 Mit relativen Imports
Vorher (flache Struktur):
# visualization.py
from .geometry import find_intersection
from .road import generate_road_profile
Nachher (verschachtelte Struktur) - ERFORDERT AUCH ÄNDERUNGEN:
# ui/visualization.py
from ..core.geometry import find_intersection
from ..core.road import generate_road_profile
Auswirkung: ❌ Muss Import-Pfade aktualisieren (aber nur Punkte ändern sich, nicht der vollständige Pfad)
6.4 Vergleichstabelle
| Aspekt | Absolute Imports | Relative Imports |
|---|---|---|
| Klarheit | ✅ Sofort klar, woher Code kommt | ⚠️ Muss Punkte mental navigieren |
| Zuverlässigkeit | ✅ Funktioniert als Skript oder Modul | ❌ Nur mit python -m |
| IDE-Unterstützung | ✅ Bessere Autovervollständigung und Navigation | ⚠️ Kann bei komplexer Verschachtelung Probleme haben |
| Refactoring | ❌ Muss vollständige Pfade bei Umstrukturierung aktualisieren | ⚠️ Muss Punkte beim Ändern von Ebenen aktualisieren |
| Package-Umbenennung | ❌ Muss alle Imports aktualisieren, wenn Package umbenannt wird | ✅ Keine Änderungen nötig |
| Lernkurve | ✅ Einfacher für Anfänger | ⚠️ Erfordert Verständnis der Package-Hierarchie |
| Ausführlichkeit | ⚠️ Längere Import-Anweisungen | ✅ Kürzer, prägnanter |
6.5 Echter Code-Vergleich
Schauen wir uns ein vollständiges Beispiel nebeneinander an:
Dateistruktur:
road_profile_viewer/
├── core/
│ ├── __init__.py
│ ├── geometry.py
│ └── road.py
├── ui/
│ ├── __init__.py
│ └── visualization.py
└── main.py
Version mit absoluten Imports:
# ui/visualization.py
from road_profile_viewer.core.geometry import find_intersection
from road_profile_viewer.core.road import generate_road_profile
import dash
def create_dash_app():
profile = generate_road_profile()
return dash.Dash(__name__)
# main.py
from road_profile_viewer.ui.visualization import create_dash_app
def main():
app = create_dash_app()
app.run_server()
if __name__ == "__main__":
main()
Version mit relativen Imports:
# ui/visualization.py
from ..core.geometry import find_intersection
from ..core.road import generate_road_profile
import dash
def create_dash_app():
profile = generate_road_profile()
return dash.Dash(__name__)
# main.py
from .ui.visualization import create_dash_app
def main():
app = create_dash_app()
app.run_server()
if __name__ == "__main__":
main()
Ausführung:
# Absolute Imports: Funktioniert auf beide Arten
python road_profile_viewer/main.py # ✅ Funktioniert
python -m road_profile_viewer.main # ✅ Funktioniert
# Relative Imports: Funktioniert nur auf eine Art
python road_profile_viewer/main.py # ❌ ImportError!
python -m road_profile_viewer.main # ✅ Funktioniert
6.6 Empfehlung für diesen Kurs
Verwenden Sie absolute Imports für die road-profile-viewer Aufgabe, weil:
- ✅ Von der Aufgabenspezifikation gefordert
- ✅ Zuverlässiger für Anfänger
- ✅ Bessere IDE-Unterstützung in VS Code
- ✅ Einfacher für Peer-Code-Review
- ✅ Konsistent mit Kursbeispielen
Format:
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
6.7 Wichtigste Erkenntnisse: Vergleich
- ✅ Absolute Imports: Explizit, zuverlässig, anfängerfreundlich
- ✅ Relative Imports: Prägnant, package-name-agnostisch, erfordert
-mFlag - ✅ Beide haben Kompromisse; Konsistenz ist am wichtigsten
- ✅ Für diesen Kurs: immer absolute Imports verwenden
- ✅ In der realen Welt: Folgen Sie dem Style Guide Ihres Teams
7. Externe Packages: Wie import numpy einfach funktioniert
Jetzt beantworten wir die brennende Frage: Warum kann ich import numpy as np schreiben, aber das funktioniert nicht mit meinen eigenen Modulen?
7.1 Das Geheimnis einfacher Imports
Sie haben dieses Muster bemerkt:
# Externe Packages: Einfache Imports ✅
import numpy as np
import pandas as pd
import dash
# Ihr eigener Code: Vollständiger Pfad erforderlich ❌
import geometry # ModuleNotFoundError!
# Ihr eigener Code: Muss vollständigen Pfad verwenden ✅
from road_profile_viewer.geometry import find_intersection
Warum der Unterschied?
7.2 Wo externe Packages leben
Wenn Sie uv add numpy ausführen, installiert uv numpy im site-packages/-Verzeichnis Ihrer virtuellen Umgebung:
.venv/
└── lib/
└── python3.12/
└── site-packages/
├── numpy/ ← numpy Package
│ ├── __init__.py
│ ├── core/
│ ├── linalg/
│ └── ...
├── pandas/ ← pandas Package
├── dash/ ← dash Package
└── plotly/ ← plotly Package
7.3 Wie Python externe Packages findet
Erinnern Sie sich an sys.path? Es enthält site-packages/:
import sys
print(sys.path)
Ausgabe:
[
'/home/student/road-profile-viewer', # Aktuelles Verzeichnis
'/home/student/.venv/lib/python3.12/site-packages', # ← Installierte Packages!
'/usr/lib/python3.12', # Standardbibliothek
]
Wenn Sie import numpy schreiben, macht Python:
- Durchsucht
sys.pathin Reihenfolge - Findet
numpy/-Verzeichnis insite-packages/ - Importiert
numpy/__init__.py - Erfolg!
7.4 Warum Ihre eigenen Module nicht so funktionieren
Ihre Projektstruktur:
road-profile-viewer/ ← Projekt-Root (in sys.path)
└── road_profile_viewer/ ← Package-Verzeichnis
├── geometry.py ← Modul
├── road.py ← Modul
└── main.py ← Modul
Wenn Sie import geometry schreiben, macht Python:
- Durchsucht
sys.path - Sucht nach
geometry.pyodergeometry/-Verzeichnis im Projekt-Root - Findet es nicht (weil es in
road_profile_viewer/ist) - ModuleNotFoundError!
Ihre Module sind in einem Package verschachtelt, also müssen Sie den vollständigen Pfad verwenden:
from road_profile_viewer.geometry import find_intersection # ✅ Funktioniert
7.5 Pythons Import-Suchreihenfolge
Wenn Sie etwas importieren, sucht Python in dieser Reihenfolge:
- Eingebaute Module (wie
sys,os,json) - Aktuelles Verzeichnis (wo Sie Python ausführen)
- Verzeichnisse in
PYTHONPATH(Umgebungsvariable) site-packages/der virtuellen Umgebung (installierte Packages)site-packages/des System-Python (globale Packages - normalerweise in venv ignoriert)- Standardbibliothek (Pythons eingebaute Module)
Beispiel-Auflösung:
import sys # ← Gefunden in eingebauten Modulen (Schritt 1)
import numpy # ← Gefunden in venv's site-packages (Schritt 4)
import geometry # ← Nirgends gefunden → ModuleNotFoundError!
7.6 Ihr eigenes Package installieren (Fortgeschritten)
Was, wenn Sie Ihren eigenen Code wie import road_profile_viewer importieren möchten?
Sie können Ihr eigenes Projekt als Package in site-packages/ installieren:
# Installieren Sie Ihr Projekt im "editierbaren" Modus
uv pip install -e .
Dies erstellt einen Link in site-packages/, der auf Ihr Projekt zeigt:
.venv/lib/python3.12/site-packages/
├── numpy/
├── dash/
└── road_profile_viewer.egg-link ← Zeigt auf Ihr Projekt!
Jetzt funktioniert dies:
# Von überall, nicht nur vom Projekt-Root:
import road_profile_viewer
from road_profile_viewer.geometry import find_intersection
Wann dies tun:
- ✅ Package an andere verteilen
- ✅ Tests aus verschiedenen Verzeichnissen ausführen
- ✅ Ihr Package in mehreren Projekten verwenden
Wann NICHT tun (für diesen Kurs):
- ❌ Nicht für die Aufgabe erforderlich
- ❌ Fügt Komplexität für Anfänger hinzu
- ❌
uv runhandhabt dies automatisch
7.7 Die Gefahr: Namenskonflikte
⚠️ Benennen Sie Ihre Module NIEMALS wie Standardbibliothek oder beliebte Packages!
Schlechte Idee:
my_project/
├── numpy.py ← ❌ Überschattet das echte numpy!
├── json.py ← ❌ Überschattet eingebautes json!
└── main.py
Was passiert:
# main.py
import numpy # ← Importiert IHR numpy.py, nicht das echte numpy Package!
Python durchsucht das aktuelle Verzeichnis zuerst, also wird Ihr numpy.py vor dem echten numpy in site-packages/ gefunden.
Ergebnis: Nichts funktioniert, und Fehlermeldungen sind verwirrend!
Wie vermeiden:
✅ Verwenden Sie beschreibende, eindeutige Namen für Ihre Module
✅ Verwenden Sie niemals Namen aus der Standardbibliothek (os, sys, json, math, etc.)
✅ Überprüfen Sie PyPI vor der Benennung eines Packages: pypi.org
7.8 Den Unterschied visualisieren
Externe Packages:
import numpy
↓
sys.path enthält .venv/lib/python3.12/site-packages/
↓
Gefunden: site-packages/numpy/
↓
Import numpy/__init__.py
↓
✅ Erfolg!
Ihre Module:
import geometry
↓
sys.path enthält /home/student/road-profile-viewer/
↓
NICHT gefunden: /home/student/road-profile-viewer/geometry.py
(weil es im road_profile_viewer/ Unterverzeichnis ist)
↓
❌ ModuleNotFoundError!
RICHTIGER WEG:
from road_profile_viewer.geometry import ...
↓
sys.path enthält /home/student/road-profile-viewer/
↓
Gefunden: /home/student/road-profile-viewer/road_profile_viewer/
↓
Gefunden: geometry.py in road_profile_viewer/
↓
✅ Erfolg!
7.9 Wichtigste Erkenntnisse: Externe Packages
- ✅ Externe Packages sind in
site-packages/installiert (in Ihrer venv) - ✅
site-packages/ist insys.path, also funktionieren einfache Imports:import numpy - ✅ Ihre Module sind in einem Package-Verzeichnis verschachtelt, also brauchen Sie den vollständigen Pfad
- ✅ Python durchsucht
sys.pathin Reihenfolge: aktuelles Verz. → venv → stdlib - ✅ Benennen Sie Ihre Module niemals wie beliebte Packages oder stdlib-Module
- ✅
uv add packageinstalliert insite-packages/,uv runstellt sicher, dass es im Pfad ist
8. Hands-On: Das Import-System Ihres Projekts erkunden
Zeit, die Hände schmutzig zu machen! Lassen Sie uns erkunden, wie Imports in Ihrem tatsächlichen Projekt funktionieren.
8.1 Übung 1: sys.path inspizieren
Erstellen Sie eine neue Datei: road_profile_viewer/debug_imports.py
"""Debug-Skript zum Verständnis des Python-Import-Systems."""
import sys
import os
print("=" * 60)
print("PYTHON IMPORT SYSTEM DEBUG")
print("=" * 60)
print("\n1. AKTUELLES ARBEITSVERZEICHNIS:")
print(f" {os.getcwd()}")
print("\n2. SKRIPT-ORT:")
print(f" {__file__}")
print("\n3. sys.path (wo Python nach Modulen sucht):")
for i, path in enumerate(sys.path, 1):
print(f" [{i}] {path}")
print("\n4. VIRTUELLE UMGEBUNG:")
venv = os.environ.get('VIRTUAL_ENV', 'Nicht aktiviert')
print(f" {venv}")
print("\n5. VERSUCHE UNSERE MODULE ZU IMPORTIEREN:")
try:
from road_profile_viewer.geometry import find_intersection
print(" ✅ Erfolgreich aus road_profile_viewer.geometry importiert")
except ImportError as e:
print(f" ❌ Import fehlgeschlagen: {e}")
print("\n6. VERSUCHE EXTERNE PACKAGES ZU IMPORTIEREN:")
try:
import numpy
print(f" ✅ numpy Version {numpy.__version__} von {numpy.__file__}")
except ImportError as e:
print(f" ❌ numpy Import fehlgeschlagen: {e}")
print("=" * 60)
Ausführen:
# Stellen Sie sicher, dass Sie im Projekt-Root sind
cd /path/to/road-profile-viewer
# Als Modul ausführen
uv run python -m road_profile_viewer.debug_imports
Erwartete Ausgabe:
============================================================
PYTHON IMPORT SYSTEM DEBUG
============================================================
1. AKTUELLES ARBEITSVERZEICHNIS:
/home/student/road-profile-viewer
2. SKRIPT-ORT:
/home/student/road-profile-viewer/road_profile_viewer/debug_imports.py
3. sys.path (wo Python nach Modulen sucht):
[1] /home/student/road-profile-viewer
[2] /home/student/road-profile-viewer/.venv/lib/python3.12/site-packages
[3] /usr/lib/python3.12
[4] /usr/lib/python3.12/lib-dynload
4. VIRTUELLE UMGEBUNG:
/home/student/road-profile-viewer/.venv
5. VERSUCHE UNSERE MODULE ZU IMPORTIEREN:
✅ Erfolgreich aus road_profile_viewer.geometry importiert
6. VERSUCHE EXTERNE PACKAGES ZU IMPORTIEREN:
✅ numpy Version 1.26.0 von /home/student/road-profile-viewer/.venv/lib/python3.12/site-packages/numpy/__init__.py
============================================================
Beobachtungen:
- Projekt-Root ist in
sys.path[0] site-packagesder virtuellen Umgebung ist insys.path[1]- Deshalb funktionieren absolute Imports!
- Externe Packages werden in
site-packages/gefunden
8.2 Übung 2: Bequeme Imports mit __init__.py hinzufügen
Erstellen/bearbeiten Sie: road_profile_viewer/__init__.py
"""
Road Profile Viewer
Ein Python-Package zur Visualisierung von Straßenhöhenprofilen.
"""
__version__ = "1.0.0"
__author__ = "Ihr Name"
# Bequeme Imports - häufig verwendete Funktionen auf Package-Ebene verfügbar machen
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
__all__ = [
'find_intersection',
'generate_road_profile',
'create_dash_app',
]
# Optional: Protokollieren, wenn Package importiert wird
import logging
logger = logging.getLogger(__name__)
logger.info(f"Road Profile Viewer v{__version__} geladen")
Testen:
uv run python
>>> import road_profile_viewer
>>> print(road_profile_viewer.__version__)
1.0.0
>>> # Jetzt können wir von der Package-Ebene importieren!
>>> from road_profile_viewer import find_intersection
>>> print(find_intersection)
<function find_intersection at 0x...>
>>> # Anstatt des längeren:
>>> from road_profile_viewer.geometry import find_intersection
Vorteile:
- Kürzere Import-Pfade
- Saubere öffentliche API
- Package-Metadaten zugänglich
8.3 Übung 3: Absolute vs. Relative Imports vergleichen
Erstellen Sie eine Test-Datei: road_profile_viewer/import_test.py
"""Teste verschiedene Import-Stile."""
print("Teste absolute Imports:")
try:
from road_profile_viewer.geometry import find_intersection
print("✅ Absoluter Import funktioniert!")
except ImportError as e:
print(f"❌ Absoluter Import fehlgeschlagen: {e}")
print("\nTeste relative Imports:")
try:
from .geometry import find_intersection
print("✅ Relativer Import funktioniert!")
except ImportError as e:
print(f"❌ Relativer Import fehlgeschlagen: {e}")
print("\nTeste einfachen Import (wird fehlschlagen):")
try:
from geometry import find_intersection
print("✅ Einfacher Import funktioniert!")
except ImportError as e:
print(f"❌ Einfacher Import fehlgeschlagen: {e}")
Als Modul testen:
uv run python -m road_profile_viewer.import_test
Erwartete Ausgabe:
Teste absolute Imports:
✅ Absoluter Import funktioniert!
Teste relative Imports:
✅ Relativer Import funktioniert!
Teste einfachen Import (wird fehlschlagen):
❌ Einfacher Import fehlgeschlagen: No module named 'geometry'
Jetzt als Skript versuchen (wird fehlschlagen):
cd road_profile_viewer
uv run python import_test.py
Erwartete Ausgabe:
Teste absolute Imports:
✅ Absoluter Import funktioniert!
Teste relative Imports:
❌ Relativer Import fehlgeschlagen: attempted relative import with no known parent package
Teste einfachen Import (wird fehlschlagen):
❌ Einfacher Import fehlgeschlagen: No module named 'geometry'
Wichtige Lektion: Relative Imports funktionieren nur, wenn mit python -m ausgeführt!
8.4 Übung 4: Wo numpy lebt erkunden
uv run python
>>> import numpy
>>> print(numpy.__file__)
/home/student/road-profile-viewer/.venv/lib/python3.12/site-packages/numpy/__init__.py
>>> # Besser: Verwenden Sie __path__, um das Package-Verzeichnis direkt zu sehen
>>> print(numpy.__path__)
['/home/student/road-profile-viewer/.venv/lib/python3.12/site-packages/numpy']
>>> # Das gesamte Package-Verzeichnis sehen
>>> import os
>>> numpy_dir = os.path.dirname(numpy.__file__)
>>> print(f"numpy ist installiert unter: {numpy_dir}")
numpy ist installiert unter: /home/student/road-profile-viewer/.venv/lib/python3.12/site-packages/numpy
>>> # Einige von numpys Modulen auflisten
>>> os.listdir(numpy_dir)[:10]
['__init__.py', '__pycache__', 'core', 'distutils', 'doc', 'f2py', 'fft', 'lib', 'linalg', 'ma']
Beobachtungen:
- Externe Packages sind nur Verzeichnisse von Python-Dateien in
site-packages/, genau wie Ihr Projekt sein könnte! __path__Attribut zeigt Ihnen genau, wo ein Package lokalisiert ist (funktioniert für Packages, nicht für Module)- Dies ist nützlich zum Debuggen von Import-Problemen oder zum Verstehen, wo Ihre Abhängigkeiten installiert sind
8.5 Übung 5: Was aus Ihrem Package importierbar ist sehen
>>> import road_profile_viewer
>>> dir(road_profile_viewer)
['__all__', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'create_dash_app', 'find_intersection', 'generate_road_profile', 'logging', 'logger']
>>> # Nur exportierte Namen (von __all__):
>>> road_profile_viewer.__all__
['find_intersection', 'generate_road_profile', 'create_dash_app']
Dies zeigt, was Ihr __init__.py verfügbar gemacht hat!
8.6 Wichtigste Erkenntnisse: Hands-On-Erkundung
- ✅
sys.pathbestimmt, wo Python nach Modulen sucht - ✅ Projekt-Root und
site-packages/sind insys.path - ✅
__init__.pyermöglicht es Ihnen, Package-Imports anzupassen - ✅ Relative Imports funktionieren nur, wenn mit
-mFlag ausgeführt - ✅ Externe Packages sind nur Verzeichnisse in
site-packages/ - ✅ Sie können Import-Orte mit
module.__file__inspizieren
9. Häufige Import-Fehler und Lösungen
Lassen Sie uns die häufigsten Import-Fehler angehen, denen Sie begegnen werden, und wie man sie behebt.
9.1 Fehler 1: ImportError: attempted relative import with no known parent package
Wann Sie es sehen:
$ python road_profile_viewer/main.py
ImportError: attempted relative import with no known parent package
Was ist passiert:
Sie verwenden relative Imports (wie from .visualization import), führen aber die Datei als Skript statt als Modul aus.
Warum es passiert:
Wenn Sie python road_profile_viewer/main.py ausführen, weiß Python nicht, dass main.py Teil des road_profile_viewer Packages ist. Relative Imports erfordern Package-Kontext.
Lösungen:
Option 1: python -m verwenden (empfohlen)
cd /path/to/project-root
python -m road_profile_viewer.main
Option 2: Stattdessen absolute Imports verwenden
# Ändern Sie dies:
from .visualization import create_dash_app
# Zu diesem:
from road_profile_viewer.visualization import create_dash_app
Option 3: uv run verwenden
uv run python -m road_profile_viewer.main
9.2 Fehler 2: ModuleNotFoundError: No module named 'road_profile_viewer'
Wann Sie es sehen:
$ python -m road_profile_viewer.main
ModuleNotFoundError: No module named 'road_profile_viewer'
Was ist passiert:
Python kann Ihr road_profile_viewer Package in sys.path nicht finden.
Warum es passiert:
- Sie führen Python aus dem falschen Verzeichnis aus
- Ihr aktuelles Verzeichnis ist nicht das Projekt-Root
- Virtuelle Umgebung ist nicht aktiviert (wenn manuelle venv verwendet wird)
Lösungen:
Option 1: Aus korrektem Verzeichnis ausführen
# Stellen Sie sicher, dass Sie im Projekt-Root sind:
cd /path/to/road-profile-viewer # Das Verzeichnis, das road_profile_viewer/ enthält
python -m road_profile_viewer.main
Option 2: Projektstruktur überprüfen
ls
# Sollte sehen:
# road_profile_viewer/ ← Dies ist das Package
# pyproject.toml
# README.md
# etc.
Option 3: uv run verwenden (handhabt dies automatisch)
uv run python -m road_profile_viewer.main
9.3 Fehler 3: ImportError: cannot import name 'find_intersection' from 'geometry'
Wann Sie es sehen:
from geometry import find_intersection
ImportError: cannot import name 'find_intersection' from 'geometry'
Was ist passiert:
Python hat ein geometry-Modul gefunden, aber es hat nicht find_intersection.
Warum es passiert:
- Sie importieren aus dem falschen Modul
- Es gibt einen Namenskonflikt (vielleicht haben Sie
geometry.pyam falschen Ort) - Der Funktionsname ist falsch geschrieben
Lösungen:
Option 1: Vollständigen Import-Pfad verwenden
# Anstatt:
from geometry import find_intersection
# Verwenden:
from road_profile_viewer.geometry import find_intersection
Option 2: Auf Namenskonflikte prüfen
# Stellen Sie sicher, dass Sie nicht mehrere geometry.py Dateien haben:
find . -name "geometry.py"
Option 3: Überprüfen, dass die Funktion existiert
# Prüfen, was tatsächlich im Modul ist:
from road_profile_viewer import geometry
print(dir(geometry))
9.4 Fehler 4: ModuleNotFoundError: No module named 'numpy' (oder anderes externes Package)
Wann Sie es sehen:
import numpy as np
ModuleNotFoundError: No module named 'numpy'
Was ist passiert:
numpy ist nicht in Ihrer virtuellen Umgebung installiert.
Warum es passiert:
- Virtuelle Umgebung ist nicht aktiviert
- Abhängigkeiten wurden nicht installiert
- Sie verwenden den falschen Python-Interpreter
Lösungen:
Option 1: Abhängigkeiten mit uv synchronisieren
uv sync
Option 2: Package manuell installieren
uv add numpy
Option 3: Prüfen, welches Python Sie verwenden
which python # Mac/Linux
where python # Windows
# Sollte auf .venv/bin/python oder .venv\Scripts\python.exe zeigen
Option 4: Immer uv run verwenden
uv run python -m road_profile_viewer.main
9.5 Fehler 5: Zirkulärer Import
Wann Sie es sehen:
ImportError: cannot import name 'function_a' from partially initialized module 'module_b'
(most likely due to a circular import)
Was ist passiert:
Zwei Module importieren voneinander, was eine Abhängigkeitsschleife erzeugt.
Beispiel:
# geometry.py
from road_profile_viewer.road import some_function
# road.py
from road_profile_viewer.geometry import find_intersection
Python kann keines der Module initialisieren, weil beide das andere initialisiert benötigen!
Lösungen:
Option 1: Code umstrukturieren (am besten)
Gemeinsame Abhängigkeiten in ein separates Modul verschieben:
# utils.py
def shared_function():
pass
# geometry.py
from road_profile_viewer.utils import shared_function
# road.py
from road_profile_viewer.utils import shared_function
Option 2: Import in Funktion verschieben
# geometry.py
def calculate_something():
# Hier importieren statt oben
from road_profile_viewer.road import some_function
return some_function()
Option 3: Type Hints mit TYPE_CHECKING verwenden
from typing import TYPE_CHECKING
if TYPE_CHECKING:
# Nur während Type-Checking importiert, nicht zur Laufzeit
from road_profile_viewer.road import RoadProfile
9.6 Fehler 6: NameError: name 'find_intersection' is not defined
Wann Sie es sehen:
distance = find_intersection(x, angle, profile)
NameError: name 'find_intersection' is not defined
Was ist passiert:
Sie haben vergessen, die Funktion zu importieren!
Lösung:
# Fügen Sie den Import am Anfang der Datei hinzu:
from road_profile_viewer.geometry import find_intersection
# Dann verwenden Sie sie:
distance = find_intersection(x, angle, profile)
9.7 Debug-Checkliste
Wenn Sie auf Import-Fehler stoßen, prüfen Sie:
- ✅ Sind Sie im richtigen Verzeichnis? (Projekt-Root)
- ✅ Verwenden Sie
python -m package.moduleanstattpython file.py? - ✅ Ist Ihre virtuelle Umgebung aktiviert? (oder verwenden Sie
uv run?) - ✅ Sind Abhängigkeiten installiert? (führen Sie
uv syncaus) - ✅ Ist der Import-Pfad korrekt? (prüfen Sie Schreibweise, Package-Struktur)
- ✅ Verwenden Sie absolute Imports? (empfohlen für diesen Kurs)
- ✅ Existiert die Funktion/Klasse tatsächlich im Modul?
9.8 Wichtigste Erkenntnisse: Import-Fehler
- ✅ Die meisten Import-Fehler kommen von falscher Code-Ausführung oder falschem Verzeichnis
- ✅ Verwenden Sie
python -m package.modulefür zuverlässige Imports - ✅ Verwenden Sie
uv run, um Umgebungsprobleme zu vermeiden - ✅ Relative Imports funktionieren nur, wenn Python die Package-Struktur kennt
- ✅ Zirkuläre Imports erfordern Code-Umstrukturierung
- ✅ Prüfen Sie immer
sys.pathbeim Debuggen:python -c "import sys; print(sys.path)"
10. Best Practices Zusammenfassung
Lassen Sie uns mit umsetzbaren Best Practices für Ihre Python-Projekte abschließen.
10.1 Import-Stil-Richtlinien
1. Verwenden Sie absolute Imports (Kursanforderung)
# ✅ GUT: Absolute Imports
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
# ❌ VERMEIDEN: Relative Imports (für diesen Kurs)
from .geometry import find_intersection
from .road import generate_road_profile
from .visualization import create_dash_app
2. Imports richtig gruppieren
# Standardbibliotheks-Imports
import os
import sys
from pathlib import Path
# Externe Package-Imports
import numpy as np
import pandas as pd
import dash
from dash import dcc, html
# Lokale Package-Imports
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
3. Spezifische Imports verwenden
# ✅ GUT: Importieren Sie, was Sie brauchen
from road_profile_viewer.geometry import find_intersection, calculate_ray_line
# ⚠️ OK aber weniger klar:
import road_profile_viewer.geometry
# Dann verwenden: road_profile_viewer.geometry.find_intersection()
# ❌ VERMEIDEN: Stern-Imports
from road_profile_viewer.geometry import * # Wer weiß, was dies importiert?
10.2 Projektstruktur-Best-Practices
1. Halten Sie __init__.py minimal
# road_profile_viewer/__init__.py
"""Road Profile Viewer Package."""
__version__ = "1.0.0"
# Nur Haupt-API verfügbar machen
from road_profile_viewer.visualization import create_dash_app
__all__ = ['create_dash_app']
2. Nach Funktionalität organisieren
road_profile_viewer/
├── __init__.py
├── main.py # Einstiegspunkt
├── core/ # Kernlogik
│ ├── __init__.py
│ ├── geometry.py
│ └── road.py
├── ui/ # Benutzeroberfläche
│ ├── __init__.py
│ └── visualization.py
└── utils/ # Hilfsprogramme
├── __init__.py
└── helpers.py
3. Überschatten Sie niemals Standardbibliotheksnamen
# ❌ SCHLECHT: Namenskonflikte
json.py # Überschattet eingebautes json
math.py # Überschattet eingebautes math
sys.py # Überschattet eingebautes sys
os.py # Überschattet eingebautes os
# ✅ GUT: Beschreibende, eindeutige Namen
json_parser.py
math_utils.py
system_info.py
os_helpers.py
10.3 Code-Ausführungs-Best-Practices
1. Verwenden Sie immer -m für Package-Module
# ✅ GUT: Als Modul ausführen
python -m road_profile_viewer.main
# ❌ SCHLECHT: Als Skript ausführen (bricht relative Imports)
python road_profile_viewer/main.py
2. uv run für Bequemlichkeit verwenden
# ✅ AM BESTEN: Lassen Sie uv alles handhaben
uv run python -m road_profile_viewer.main
# ✅ AUCH GUT: Skripte ausführen, die in pyproject.toml definiert sind
uv run road-viewer
3. Vom Projekt-Root ausführen
# Führen Sie Befehle immer aus dem Verzeichnis aus, das Ihr Package enthält:
/path/to/road-profile-viewer/ ← Hier ausführen
├── road_profile_viewer/ ← Ihr Package
├── pyproject.toml
└── README.md
10.4 Abhängigkeitsmanagement-Best-Practices
1. Verwenden Sie pyproject.toml als einzige Quelle der Wahrheit
[project]
name = "road-profile-viewer"
version = "1.0.0"
dependencies = [
"dash>=2.14.0",
"plotly>=5.18.0",
"pandas>=2.1.0",
"numpy>=1.26.0",
]
2. Trennen Sie Entwicklungsabhängigkeiten
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"ruff>=0.1.0",
"pyright>=1.1.0",
]
3. Verwenden Sie Versionsbeschränkungen weise
dependencies = [
"numpy>=1.26.0", # ✅ Patch/Minor-Updates erlauben
"dash>=2.14,<3.0", # ✅ Major-Version sperren
"pandas==2.1.0", # ⚠️ Zu streng, vermeiden Sie es, wenn nicht notwendig
]
10.5 Ihre Imports testen
Erstellen Sie ein Test-Skript, um zu überprüfen, dass alles funktioniert:
# test_imports.py
"""Überprüfen, dass alle Imports funktionieren."""
def test_imports():
"""Testen, dass alle Haupt-Imports funktionieren."""
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
from road_profile_viewer.visualization import create_dash_app
print("✅ Alle Imports erfolgreich!")
# Überprüfen, dass Funktionen aufrufbar sind
assert callable(find_intersection)
assert callable(generate_road_profile)
assert callable(create_dash_app)
print("✅ Alle Funktionen sind aufrufbar!")
if __name__ == "__main__":
test_imports()
Ausführen:
uv run python -m test_imports
10.6 Dokumentations-Best-Practices
1. Modulzweck dokumentieren
# geometry.py
"""
Geometrische Berechnungen für Strahl-Linien-Schnittpunkte.
Dieses Modul bietet Funktionen zur Berechnung von Schnittpunkten zwischen
Strahlen und Straßenprofil-Liniensegmenten.
"""
2. Import-Abhängigkeiten dokumentieren
# visualization.py
"""
Interaktive Dash-Webanwendung zur Straßenprofil-Visualisierung.
Abhängigkeiten:
- dash: Webanwendungs-Framework
- plotly: Interaktives Plotting
- road_profile_viewer.geometry: Strahl-Schnittberechnungen
- road_profile_viewer.road: Straßenprofil-Generierung
"""
10.7 Schnellreferenz: Import-Muster
Ihre eigenen Module:
# ✅ Verwenden Sie absolute Imports
from road_profile_viewer.geometry import find_intersection
from road_profile_viewer.road import generate_road_profile
Externe Packages:
# ✅ Einfache Imports
import numpy as np
import pandas as pd
Standardbibliothek:
# ✅ Einfache Imports
import os
import sys
from pathlib import Path
Ihren Code ausführen:
# ✅ Als Modul (vom Projekt-Root)
uv run python -m road_profile_viewer.main
# ✅ Skripte verwenden, die in pyproject.toml definiert sind
uv run road-viewer
10.8 Abschließende Checkliste für die Aufgabe
Vor der Abgabe Ihres refaktorierten road-profile-viewer:
- ✅ Alle Imports sind absolut:
from road_profile_viewer.module import ... - ✅ Keine relativen Imports:
from .module import ... - ✅ Code läuft mit:
uv run python -m road_profile_viewer.main - ✅ Keine Namenskonflikte mit Standardbibliothek oder externen Packages
- ✅
pyproject.tomllistet alle Abhängigkeiten korrekt auf - ✅ Virtuelle Umgebung kann mit
uv syncneu erstellt werden - ✅ Code besteht
ruff checkundpyright(keine Import-Fehler)
10.9 Wichtigste Erkenntnisse: Best Practices
- ✅ Konsistenz: Wählen Sie absolute Imports und verwenden Sie sie überall
- ✅ Klarheit: Verwenden Sie spezifische Imports, vermeiden Sie
import * - ✅ Organisation: Gruppieren Sie Imports logisch (stdlib, extern, lokal)
- ✅ Zuverlässigkeit: Führen Sie mit
python -moderuv runaus - ✅ Sicherheit: Überschatten Sie niemals Standardbibliotheksnamen
- ✅ Einfachheit: Halten Sie
__init__.pyminimal - ✅ Dokumentation: Erklären Sie Abhängigkeiten in Modul-Docstrings
11. Fazit: Sie verstehen jetzt Pythons Import-System
Herzlichen Glückwunsch! Sie haben eine umfassende Reise durch Pythons Import-System abgeschlossen. Lassen Sie uns zusammenfassen, was Sie gelernt haben:
11.1 Was Sie jetzt wissen
- ✅ Module: Jede
.py-Datei ist ein Modul mit eigenem Namespace - ✅ Packages: Verzeichnisse mit Modulen, hierarchisch organisiert
- ✅
__init__.py: Optional in Python 3.3+, aber nützlich für Initialisierung und bequeme Imports - ✅ Absolute Imports: Verwenden vollständige Pfade vom Package-Root (
from road_profile_viewer.geometry import ...) - ✅ Relative Imports: Verwenden Punkte zur Navigation in Package-Hierarchie (
.= aktuell,..= Eltern) - ✅ Externe Packages: Installiert in
site-packages/, importierbar mit einfachen Namen (import numpy) - ✅ sys.path: Pythons Suchpfad bestimmt, wo Module gefunden werden
- ✅ Häufige Fehler: Wie man Import-Probleme diagnostiziert und behebt
- ✅ Best Practices: Richtlinien für professionelle Python-Projekte
11.2 Warum das für Ihre Karriere wichtig ist
Das Verständnis von Pythons Import-System ist fundamental, weil:
- ✅ Jedes reale Python-Projekt mehrere Module und Packages verwendet
- ✅ Zusammenarbeit mit Teams konsistente Import-Konventionen erfordert
- ✅ Debuggen von Import-Fehlern eine tägliche Aufgabe für professionelle Entwickler ist
- ✅ Strukturierung großer Codebasen von richtiger Package-Organisation abhängt
- ✅ Beitrag zu Open Source bedeutet, vorhandene Import-Strukturen zu lesen und zu verstehen
11.3 Wie dies auf Ihre Aufgabe zutrifft
Für die road-profile-viewer Refactoring-Aufgabe:
- ✅ Verwenden Sie absolute Imports ausschließlich:
from road_profile_viewer.module import function - ✅ Führen Sie mit
python -m road_profile_viewer.mainoderuv run python -m road_profile_viewer.mainaus - ✅ Halten Sie Ihre Package-Struktur flach (keine Unterverzeichnisse nötig)
- ✅ Optional fügen Sie
__init__.pyfür Package-Metadaten und bequeme Imports hinzu - ✅ Stellen Sie sicher, dass alle Imports funktionieren, indem Sie mit
uv sync && uv run python -m road_profile_viewer.maintesten
11.4 Weiterführende Literatur (optional)
Wenn Sie tiefer in Pythons Import-System eintauchen möchten:
- PEP 420 - Implicit Namespace Packages: https://peps.python.org/pep-0420/
- Python Import System Documentation: https://docs.python.org/3/reference/import.html
- Real Python - Absolute vs Relative Imports: https://realpython.com/absolute-vs-relative-python-imports/
- PEP 8 - Import Style Guide: https://peps.python.org/pep-0008/#imports
11.5 Ressourcen für die Aufgabe
- Road Profile Viewer Template: https://github.com/hs-aalen-software-engineering/road-profile-viewer-class-template
- Chapter 02 (Refactoring) - Refactoring: Überprüfen Sie die Vorlesung über Refactoring vom Monolithen zu Modulen
- uv Documentation: https://docs.astral.sh/uv/
11.6 Praxisübungen (optional)
Möchten Sie Ihr Verständnis festigen? Versuchen Sie diese Übungen:
Übung 1: Weitere Module hinzufügen
Erstellen Sie ein neues Modul road_profile_viewer/config.py mit Konfigurationskonstanten:
# config.py
"""Konfigurationskonstanten für die Anwendung."""
DEFAULT_NUM_POINTS = 100
DEFAULT_RAY_ANGLE = 45
DEFAULT_PORT = 8050
Importieren und verwenden Sie es in Ihren anderen Modulen.
Übung 2: Ein Utilities-Package erstellen
Organisieren Sie Hilfsfunktionen in ein Sub-Package:
road_profile_viewer/
├── utils/
│ ├── __init__.py
│ ├── math_helpers.py
│ └── plotting_helpers.py
Übung 3: Package-Metadaten hinzufügen
Erstellen Sie ein umfassendes __init__.py mit Versionsinformationen, bequemen Imports und Logging-Setup.
Übung 4: Import-Stile testen
Erstellen Sie Branches in Ihrem Git-Repo zum Experimentieren:
- Branch 1: Reine absolute Imports
- Branch 2: Reine relative Imports
- Branch 3: Gemischt (sehen Sie, was bricht!)
12. Nächste Schritte
Jetzt, da Sie Pythons Import-System verstehen, sind Sie bereit:
- ✅ Ihr road-profile-viewer Refactoring mit Zuversicht abzuschließen
- ✅ Größere Python-Projekte professionell zu strukturieren
- ✅ Import-Fehler schnell und effektiv zu debuggen
- ✅ Mit Teams mit konsistenten Konventionen zusammenzuarbeiten
- ✅ Open-Source-Python-Codebasen zu lesen und zu verstehen
Viel Erfolg beim Programmieren, und mögen alle Ihre Imports erfolgreich aufgelöst werden! 🐍
Dieser Anhang ist Teil des Software Engineering Kurses an der Hochschule Aalen (Wintersemester 2025/26). Für Fragen oder Feedback wenden Sie sich an das Kursforum oder die Sprechstunden.