Python & AI Tutorials Logo
Python Programmierung

22. Code mit Modulen und Paketen organisieren

Wenn Ihre Python-Programme größer werden, wird es unpraktisch, den gesamten Code in einer einzigen Datei zu behalten. Dann möchten Sie verwandte Funktionen, Klassen und Variablen in separate Dateien organisieren, die in verschiedenen Programmen wiederverwendet werden können. Das Modul (module)- und Paket (package)-System von Python bietet genau diese Möglichkeit – eine Möglichkeit, Code effektiv zu organisieren, zu teilen und wiederzuverwenden.

In diesem Kapitel werden wir untersuchen, wie Pythons Importsystem funktioniert, wie Sie Ihre eigenen Module erstellen und verwenden und wie Sie mehrere Module in Paketen organisieren. Wir werden außerdem die spezielle Variable __name__ betrachten, mit der Sie Dateien schreiben können, die sowohl als importierbare Module als auch als eigenständige Skripte funktionieren.

22.1) Was Module sind und wie import funktioniert

Module verstehen

Ein Modul ist einfach eine Python-Datei, die Definitionen und Anweisungen enthält. Jede .py-Datei, die Sie erstellen, ist ein Modul. Wenn Sie eine Funktion in eine Datei namens calculator.py schreiben, wird diese Datei zu einem Modul namens calculator, das andere Python-Dateien verwenden können.

Module dienen mehreren wichtigen Zwecken:

  • Wiederverwendbarkeit von Code: Schreiben Sie eine Funktion einmal und verwenden Sie sie in mehreren Programmen
  • Organisation: Gruppieren Sie zusammengehörige Funktionalität
  • Namespace-Verwaltung: Halten Sie Namen getrennt, um Konflikte zu vermeiden
  • Wartbarkeit: Kleinere, fokussierte Dateien sind leichter zu verstehen und zu ändern

Erstellen wir ein einfaches Modul, um zu sehen, wie das funktioniert. Erstellen Sie eine Datei namens greetings.py:

python
# greetings.py
def say_hello(name):
    """Return a friendly greeting."""
    return f"Hello, {name}!"
 
def say_goodbye(name):
    """Return a farewell message."""
    return f"Goodbye, {name}. See you soon!"
 
# Eine Variable auf Modul-Ebene
default_greeting = "Welcome"

Diese Datei ist jetzt ein Modul. Sie enthält zwei Funktionen und eine Variable, die andere Python-Dateien verwenden können.

Die import-Anweisung

Um Code aus einem Modul zu verwenden, importieren(import) Sie es. Die import-Anweisung sagt Python, dass es ein Modul laden und dessen Inhalte verfügbar machen soll. Erstellen Sie eine weitere Datei im selben Verzeichnis namens main.py:

python
# main.py
import greetings
 
message = greetings.say_hello("Alice")
print(message)  # Output: Hello, Alice!
 
farewell = greetings.say_goodbye("Bob")
print(farewell)  # Output: Goodbye, Bob. See you soon!
 
print(greetings.default_greeting)  # Output: Welcome

Wenn Sie main.py ausführen, führt Python die Anweisung import greetings aus. Folgendes passiert dabei im Hintergrund:

Nein

Ja

import greetings

Ist greetings
bereits importiert?

Suche nach greetings.py

Führe greetings.py
von oben nach unten aus

Erzeuge ein Modulobjekt
mit dem Namen 'greetings'

Speichere Funktionen und Variablen
als Attribute

Mache 'greetings' verfügbar
im aktuellen Namespace

Verwende vorhandenes
Modulobjekt

Wichtig: Python führt den Code eines Moduls nur beim ersten Import in ein Programm aus. Spätere Importe im selben Programm verwenden das bereits geladene Modul erneut. Das verhindert doppelte Ausführung und spart Zeit.

Auf Inhalte eines Moduls zugreifen

Nach dem Import eines Moduls greifen Sie mit Punktnotation (dot notation) auf dessen Inhalte zu: module_name.item_name. Das ist ähnlich dazu, wie wir auf String-Methoden wie text.upper() oder Listenmethoden wie numbers.append() zugreifen, wie wir in Kapitel 5 und 14 gelernt haben.

python
import greetings
 
# Auf Funktionen zugreifen
result = greetings.say_hello("Charlie")
 
# Auf Variablen zugreifen
greeting = greetings.default_greeting
 
# Sie können sogar prüfen, was in einem Modul enthalten ist
print(dir(greetings))  # Output: Listet alle im Modul definierten Namen auf

Die Punktnotation macht deutlich, woher jeder Name kommt. Wenn Sie greetings.say_hello() sehen, wissen Sie sofort, dass diese Funktion aus dem Modul greetings stammt.

Modul-Suchpfad

Wenn Sie import greetings schreiben, wie findet Python dann greetings.py? Python sucht nach Modulen in einer bestimmten Reihenfolge:

  1. Aktuelles Verzeichnis: Das Verzeichnis, das das Skript enthält, das Sie ausführen
  2. PYTHONPATH: Verzeichnisse, die in der Umgebungsvariable PYTHONPATH aufgeführt sind (falls gesetzt)
  3. Standardbibliothek (standard library): Pythons eingebaute Modulverzeichnisse
  4. Site-Packages: Drittanbieter-Pakete, die mit pip installiert wurden

Sie können Pythons Suchpfad sehen, indem Sie sys.path betrachten:

python
import sys
 
for path in sys.path:
    print(path)

Ausgabe (Beispiel – Ihre tatsächlichen Pfade unterscheiden sich je nach System und Python-Installation):

/home/user/projects/myproject
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/usr/local/lib/python3.11/site-packages

Der erste Pfad in der Ausgabe ist das aktuelle Arbeitsverzeichnis. Python durchsucht dieses Verzeichnis zuerst, sodass es Module im selben Verzeichnis finden kann.

Modulnamen und Dateinamen

Der Modulname ist der Dateiname ohne die Erweiterung .py. Wenn Ihre Datei string_utils.py heißt, dann ist der Modulname string_utils. Modulnamen müssen den Regeln für Python-Bezeichner folgen (wie wir in Kapitel 3 gelernt haben):

  • Mit einem Buchstaben oder Unterstrich beginnen
  • Nur Buchstaben, Ziffern und Unterstriche enthalten
  • Dürfen keine Python-Schlüsselwörter sein
python
# Gültige Modulnamen (und Dateinamen)
import data_processor      # data_processor.py
import user_auth          # user_auth.py
import _internal_helpers  # _internal_helpers.py
 
# Ungültig – würde Fehler verursachen
# import 2d_graphics       # Can't start with digit
# import my-module         # Hyphens not allowed
# import class             # 'class' is a keyword

Häufige Stolperfalle: Standardbibliotheks-Module überschatten

Achten Sie darauf, Ihre Module nicht genauso zu nennen wie Module der Standardbibliothek. Wenn Sie in Ihrem Projektverzeichnis eine Datei namens random.py erstellen, importiert Python Ihre Datei statt des random-Moduls der Standardbibliothek, was zu verwirrenden Fehlern führen kann:

python
# Ihre Datei: random.py
def my_function():
    return 42
 
# Eine andere Datei in Ihrem Projekt
import random
print(random.randint(1, 6))  # ERROR! Your random.py doesn't have randint()

Um das zu vermeiden, prüfen Sie, ob ein Name bereits von der Standardbibliothek verwendet wird, bevor Sie ein Modul mit diesem Namen erstellen. Sie können das prüfen, indem Sie versuchen, es in der interaktiven Python-Shell zu importieren. Wenn es ohne Fehler importiert, ist dieser Name bereits vergeben.

Was beim Import passiert

Schauen wir uns an, was tatsächlich passiert, wenn Sie ein Modul importieren. Erstellen Sie eine Datei namens demo_module.py:

python
# demo_module.py
print("Module is being loaded!")
 
def greet():
    print("Hello from demo_module")
 
print("Module loading complete!")

Importieren Sie es nun:

python
# test_import.py
print("Before import")
import demo_module
print("After import")
 
demo_module.greet()

Output:

Before import
Module is being loaded!
Module loading complete!
After import
Hello from demo_module

Beachten Sie, dass die print()-Anweisungen in demo_module.py während des Imports ausgeführt werden. Das zeigt, dass das Importieren eines Moduls seinen gesamten Code auf oberster Ebene ausführt. Funktionsdefinitionen werden zur späteren Verwendung gespeichert, aber jeglicher Code außerhalb von Funktionen wird sofort ausgeführt.

Wenn Sie dasselbe Modul im selben Programm erneut importieren, erscheinen die Lade-Meldungen nicht noch einmal:

python
import demo_module  # First import - executes module code
import demo_module  # Second import - uses cached module
import demo_module  # Third import - still uses cached module

Output:

Module is being loaded!
Module loading complete!

Der Modulcode wird nur einmal ausgeführt, egal wie oft Sie ihn importieren.

22.2) Verschiedene Arten zu importieren: import, from und as

Python bietet mehrere Möglichkeiten, Module und deren Inhalte zu importieren. Jeder Ansatz hat unterschiedliche Auswirkungen darauf, wie Sie auf importierte Namen zugreifen und wie sie Ihren Namespace beeinflussen.

Einfache import-Anweisung

Die grundlegende import-Anweisung, die wir bereits gesehen haben, lädt das gesamte Modul:

python
import math
 
result = math.sqrt(16)
print(result)  # Output: 4.0
 
pi_value = math.pi
print(pi_value)  # Output: 3.141592653589793

Bei diesem Ansatz verwenden Sie immer den Modulnamen als Präfix. Das macht Code sehr klar – Sie können immer erkennen, woher ein Name stammt.

Bestimmte Namen mit from importieren

Manchmal benötigen Sie nur ein oder zwei Elemente aus einem Modul. Die from-Anweisung erlaubt es Ihnen, bestimmte Namen direkt in Ihren Namespace zu importieren:

python
from math import sqrt, pi
 
result = sqrt(25)  # No 'math.' prefix needed
print(result)  # Output: 5.0
 
print(pi)  # Output: 3.141592653589793

Jetzt können Sie sqrt und pi direkt ohne das math.-Präfix verwenden. Das ist praktisch, wenn Sie diese Namen häufig nutzen.

Sehen wir uns ein weiteres Beispiel mit unserem greetings-Modul an:

python
# Using from import
from greetings import say_hello
 
message = say_hello("Diana")  # Direct access
print(message)  # Output: Hello, Diana!
 
# However, say_goodbye is not available since we didn't import it
# say_goodbye("Diana")  # NameError: name 'say_goodbye' is not defined

Sie können mehrere Namen in einer Anweisung importieren:

python
from greetings import say_hello, say_goodbye, default_greeting
 
print(say_hello("Eve"))      # Output: Hello, Eve!
print(say_goodbye("Frank"))  # Output: Goodbye, Frank!
print(default_greeting)      # Output: Welcome

Der Wildcard-Import (und warum Sie ihn vermeiden sollten)

Python erlaubt es, alles aus einem Modul mit * zu importieren:

python
from math import *
 
print(sqrt(9))   # Output: 3.0
print(cos(0))    # Output: 1.0
print(pi)        # Output: 3.141592653589793

Das importiert alle öffentlichen Namen aus dem Modul (Namen, die nicht mit Unterstrich beginnen). Auch wenn das praktisch wirkt, gilt es im Allgemeinen als schlechte Praxis, weil:

  1. Verschmutzung des Namespace: Sie wissen nicht genau, welche Namen Sie importieren
  2. Namenskonflikte: Importierte Namen könnten Ihre eigenen Variablen überschreiben
  3. Lesbarkeit: Leserinnen und Leser des Codes können nicht erkennen, woher Namen stammen
python
# Problematisches Beispiel
from math import *
 
# Später in Ihrem Code ...
def sqrt(x):
    """Your own square root function."""
    return x ** 0.5
 
# Which sqrt are you using? Yours or math's?
result = sqrt(16)  # Confusing!

Best Practice: Importieren Sie spezifische Namen oder verwenden Sie die einfache import-Anweisung. Vermeiden Sie from module import * außer in interaktiven Python-Sitzungen, in denen Sie experimentieren.

Importe mit as umbenennen

Manchmal sind Modul- oder Funktionsnamen lang, oder Sie möchten Namenskonflikte vermeiden. Das Schlüsselwort as erlaubt es Ihnen, einen Alias zu erstellen:

python
import math as m
 
result = m.sqrt(36)
print(result)  # Output: 6.0

Das ist besonders nützlich bei Modulen mit langen Namen oder wenn Sie gängigen Konventionen folgen:

python
import datetime as dt
 
today = dt.date.today()
print(today)  # Output: 2025-12-19 (or current date)

Sie können auch bestimmte Importe umbenennen:

python
from math import sqrt as square_root
 
result = square_root(49)
print(result)  # Output: 7.0

Das ist hilfreich, wenn Sie Namenskonflikte haben:

python
from math import sqrt as math_sqrt
 
def sqrt(x):
    """Custom square root with input validation."""
    if x < 0:
        return None
    return math_sqrt(x)
 
print(sqrt(25))   # Output: 5.0 (your function)
print(sqrt(-4))   # Output: None (your function)

Import-Stile kombinieren

Sie können verschiedene Import-Stile in derselben Datei mischen:

python
import math
from datetime import date, time
from random import randint as random_int
 
# Use math with prefix
radius = 5
area = math.pi * radius ** 2
 
# Use date and time directly
today = date.today()
current_time = time(14, 30)
 
# Use renamed function
dice_roll = random_int(1, 6)

Den richtigen Import-Stil auswählen

Hier ist eine Entscheidungshilfe:

Verwenden Sie import module, wenn:

  • Sie mehrere Elemente aus dem Modul benötigen
  • Sie maximale Klarheit darüber wollen, woher Namen stammen
  • Der Modulname kurz und klar ist

Verwenden Sie from module import name, wenn:

  • Sie nur ein oder zwei bestimmte Elemente benötigen
  • Die Namen eindeutig sind und vermutlich nicht kollidieren
  • Sie die Namen häufig verwenden werden

Verwenden Sie import module as alias, wenn:

  • Der Modulname sehr lang ist
  • Sie einer gängigen Konvention folgen (wie import numpy as np)
  • Sie Konflikte mit anderen Modulen vermeiden müssen

Vermeiden Sie from module import * in Produktionscode:

  • Verwenden Sie es nur für schnelle Experimente in der interaktiven Shell
  • Verwenden Sie es niemals in Modulen, die andere importieren werden

Sehen wir uns ein vollständiges Beispiel an, das gute Import-Praktiken demonstriert:

python
# data_processor.py
import math
from statistics import mean, median
from datetime import datetime as dt
 
def calculate_statistics(numbers):
    """Calculate various statistics for a list of numbers."""
    if not numbers:
        return None
    
    avg = mean(numbers)
    mid = median(numbers)
    std_dev = math.sqrt(sum((x - avg) ** 2 for x in numbers) / len(numbers))
    
    return {
        'mean': avg,
        'median': mid,
        'std_dev': std_dev,
        'timestamp': dt.now()
    }
 
# Test the function
data = [10, 20, 30, 40, 50]
stats = calculate_statistics(data)
print(f"Mean: {stats['mean']}")      # Output: Mean: 30.0
print(f"Median: {stats['median']}")  # Output: Median: 30
print(f"Std Dev: {stats['std_dev']:.2f}")  # Output: Std Dev: 14.14

Dieses Beispiel zeigt:

  • import math für das vollständige Modul (wir könnten später andere math-Funktionen verwenden)
  • from statistics import mean, median für bestimmte Funktionen, die wir häufig verwenden
  • from datetime import datetime as dt für ein Modul, das häufig mit Alias importiert wird

22.3) Überblick über gängige Module der Python-Standardbibliothek

Python kommt mit einer umfangreichen Standardbibliothek – einer Sammlung von Modulen, die Lösungen für häufige Programmieraufgaben bereitstellen. Diese Module sind immer verfügbar; Sie müssen nichts extra installieren. Wenn Sie verstehen, was in der Standardbibliothek verfügbar ist, hilft Ihnen, das „Rad nicht neu zu erfinden“.

Das math-Modul

Das math-Modul bietet mathematische Funktionen über die grundlegende Arithmetik hinaus:

python
import math
 
# Trigonometric functions
angle_rad = math.radians(45)  # Convert degrees to radians
print(math.sin(angle_rad))    # Output: 0.7071067811865476
print(math.cos(angle_rad))    # Output: 0.7071067811865475
 
# Rounding and absolute value
print(math.ceil(4.2))   # Output: 5 (round up)
print(math.floor(4.8))  # Output: 4 (round down)
print(math.fabs(-7.5))  # Output: 7.5 (absolute value as float)
 
# Exponential and logarithmic
print(math.exp(2))      # Output: 7.38905609893065 (e^2)
print(math.log(100))    # Output: 4.605170185988092 (natural log)
print(math.log10(100))  # Output: 2.0 (base-10 log)
 
# Constants
print(math.pi)  # Output: 3.141592653589793
print(math.e)   # Output: 2.718281828459045

Wie wir in Kapitel 4 gelernt haben, ist das math-Modul für fortgeschrittene mathematische Operationen essenziell.

Das random-Modul

Das random-Modul erzeugt pseudozufällige Zahlen und trifft zufällige Auswahlen:

python
import random
 
# Random integers
dice = random.randint(1, 6)  # Random integer from 1 to 6 (inclusive)
print(f"Dice roll: {dice}")
 
# Random floats
probability = random.random()  # Random float from 0.0 to 1.0
print(f"Probability: {probability:.4f}")
 
# Random choice from a sequence
colors = ['red', 'blue', 'green', 'yellow']
chosen_color = random.choice(colors)
print(f"Chosen color: {chosen_color}")
 
# Shuffle a list in place
deck = ['A', 'K', 'Q', 'J', '10']
random.shuffle(deck)
print(f"Shuffled deck: {deck}")
 
# Random sample without replacement
lottery_numbers = random.sample(range(1, 50), 6)
print(f"Lottery numbers: {sorted(lottery_numbers)}")

Output (example - will vary due to randomness):

Dice roll: 4
Probability: 0.7382
Chosen color: green
Shuffled deck: ['Q', 'A', '10', 'K', 'J']
Lottery numbers: [7, 15, 23, 31, 38, 42]

Das datetime-Modul

Das datetime-Modul verarbeitet Daten und Zeiten:

python
from datetime import date, time, datetime, timedelta
 
# Current date and time
today = date.today()
now = datetime.now()
print(f"Today: {today}")  # Output: Today: 2025-12-19
print(f"Now: {now}")      # Output: Now: 2025-12-19 14:30:45.123456
 
# Creating specific dates and times
birthday = date(1990, 5, 15)
meeting_time = time(14, 30)
appointment = datetime(2025, 12, 25, 10, 0)
 
print(f"Birthday: {birthday}")          # Output: Birthday: 1990-05-15
print(f"Meeting: {meeting_time}")       # Output: Meeting: 14:30:00
print(f"Appointment: {appointment}")    # Output: Appointment: 2025-12-25 10:00:00
 
# Date arithmetic with timedelta
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
print(f"Tomorrow: {tomorrow}")    # Output: Tomorrow: 2025-12-20
print(f"Next week: {next_week}")  # Output: Next week: 2025-12-26
 
# Extracting components
print(f"Year: {today.year}")      # Output: Year: 2025
print(f"Month: {today.month}")    # Output: Month: 12
print(f"Day: {today.day}")        # Output: Day: 19

Das os-Modul

Das os-Modul stellt Betriebssystem-Funktionalität bereit. Wir werden das in Kapitel 26 ausführlich behandeln, aber hier ist eine Vorschau:

python
import os
 
# Current working directory
current_dir = os.getcwd()
print(f"Current directory: {current_dir}")
 
# List files in a directory
files = os.listdir('.')
print(f"Files: {files[:3]}")  # Show first 3 files
 
# Check if a path exists
exists = os.path.exists('myfile.txt')
print(f"File exists: {exists}")
 
# Join path components (works across operating systems)
file_path = os.path.join('data', 'users', 'profile.txt')
print(f"Path: {file_path}")  # Output: data/users/profile.txt (or data\users\profile.txt on Windows)

Das sys-Modul

Das sys-Modul stellt systemspezifische Parameter und Funktionen bereit:

python
import sys
 
# Python version information
print(f"Python version: {sys.version}")
print(f"Version info: {sys.version_info}")
 
# Platform information
print(f"Platform: {sys.platform}")  # Output: linux, darwin, win32, etc.
 
# Maximum integer size
print(f"Max int: {sys.maxsize}")

Das statistics-Modul

Das statistics-Modul bietet Funktionen für statistische Berechnungen:

python
import statistics
 
grades = [85, 92, 78, 90, 88, 95, 82]
 
# Central tendency
avg = statistics.mean(grades)
mid = statistics.median(grades)
mode_val = statistics.mode([1, 2, 2, 3, 3, 3, 4])
 
print(f"Mean: {avg}")      # Output: Mean: 87.14285714285714
print(f"Median: {mid}")    # Output: Median: 88
print(f"Mode: {mode_val}") # Output: Mode: 3
 
# Spread
std_dev = statistics.stdev(grades)
variance = statistics.variance(grades)
 
print(f"Standard deviation: {std_dev:.2f}")  # Output: Standard deviation: 5.90
print(f"Variance: {variance:.2f}")           # Output: Variance: 34.81

Das collections-Modul

Das collections-Modul bietet spezialisierte Container-Typen. Wir werden das in Kapitel 39 genauer betrachten, aber hier ein kleiner Vorgeschmack:

python
from collections import Counter, defaultdict
 
# Counter - count occurrences
text = "hello world"
letter_counts = Counter(text)
print(letter_counts)  # Output: Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
print(letter_counts['l'])  # Output: 3
 
# defaultdict - dictionary with default values
word_lists = defaultdict(list)
word_lists['fruits'].append('apple')
word_lists['fruits'].append('banana')
word_lists['vegetables'].append('carrot')
print(dict(word_lists))  # Output: {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']}

Weitere Module der Standardbibliothek finden

Pythons Standardbibliothek enthält über 200 Module. Sie können sie auf verschiedene Arten erkunden:

python
# See all available modules (this takes a moment)
help('modules')
 
# Get help on a specific module
import math
help(math)
 
# See what's in a module
import random
print(dir(random))

Die Python-Dokumentation (https://docs.python.org/3/library/) bietet umfassende Informationen zu jedem Modul der Standardbibliothek. Wenn Sie mehr Erfahrung sammeln, werden Sie herausfinden, welche Module für Ihre Arbeit am nützlichsten sind.

22.4) Eigene Module erstellen und verwenden

Eigene Module zu erstellen ist unkompliziert – jede Python-Datei kann ein Modul sein. Der Schlüssel liegt darin, Ihren Code durchdacht zu organisieren, sodass Module fokussiert, wiederverwendbar und leicht zu verstehen sind.

Ein einfaches Modul erstellen

Erstellen wir ein Modul zum Arbeiten mit Schülernoten. Erstellen Sie eine Datei namens grade_calculator.py:

python
# grade_calculator.py
"""Module for calculating and analyzing student grades."""
 
def calculate_average(grades):
    """Calculate the average of a list of grades."""
    if not grades:
        return 0
    return sum(grades) / len(grades)
 
def get_letter_grade(numeric_grade):
    """Convert a numeric grade to a letter grade."""
    if numeric_grade >= 90:
        return 'A'
    elif numeric_grade >= 80:
        return 'B'
    elif numeric_grade >= 70:
        return 'C'
    elif numeric_grade >= 60:
        return 'D'
    else:
        return 'F'
 
def find_highest(grades):
    """Find the highest grade in a list."""
    if not grades:
        return None
    return max(grades)
 
def find_lowest(grades):
    """Find the lowest grade in a list."""
    if not grades:
        return None
    return min(grades)
 
# Konstanten auf Modul-Ebene
PASSING_GRADE = 60
HONOR_ROLL_THRESHOLD = 90

Erstellen Sie nun eine weitere Datei, um dieses Modul zu verwenden:

python
# student_report.py
import grade_calculator
 
# Test scores des Schülers
test_scores = [85, 92, 78, 88, 95]
 
# Statistiken berechnen
average = grade_calculator.calculate_average(test_scores)
letter = grade_calculator.get_letter_grade(average)
highest = grade_calculator.find_highest(test_scores)
lowest = grade_calculator.find_lowest(test_scores)
 
# Bericht erzeugen
print("Student Grade Report")
print("=" * 40)
print(f"Test Scores: {test_scores}")
print(f"Average: {average:.1f}")
print(f"Letter Grade: {letter}")
print(f"Highest Score: {highest}")
print(f"Lowest Score: {lowest}")
 
# Auf Honor Roll prüfen
if average >= grade_calculator.HONOR_ROLL_THRESHOLD:
    print("Status: HONOR ROLL!")
elif average >= grade_calculator.PASSING_GRADE:
    print("Status: Passing")
else:
    print("Status: Needs Improvement")

Output:

Student Grade Report
========================================
Test Scores: [85, 92, 78, 88, 95]
Average: 87.6
Letter Grade: B
Highest Score: 95
Lowest Score: 78
Status: Passing

Modul-Dokumentation

Beachten Sie den Docstring am Anfang von grade_calculator.py. Dieser Docstring auf Modul-Ebene beschreibt, was das Modul macht. Er erscheint, wenn jemand help() verwendet:

python
import grade_calculator
help(grade_calculator)

Das zeigt die Modul-Dokumentation an, einschließlich des Modul-Docstrings und aller Funktions-Docstrings. Gute Dokumentation macht Ihre Module leichter nutzbar.

Variablen und Konstanten auf Modul-Ebene

Module können Variablen enthalten, die über alle Verwendungen des Moduls hinweg geteilt werden. Diese werden oft für Konfiguration oder Konstanten verwendet:

python
# config.py
"""Application configuration settings."""
 
# Database settings
DB_HOST = "localhost"
DB_PORT = 5432
DB_NAME = "myapp"
 
# Application settings
MAX_LOGIN_ATTEMPTS = 3
SESSION_TIMEOUT = 1800  # seconds
DEBUG_MODE = False
 
# File paths
DATA_DIR = "/var/data"
LOG_DIR = "/var/log"
 
# Feature flags
ENABLE_CACHING = True
ENABLE_LOGGING = True

Konfiguration aus einem Modul verwenden:

python
# app.py
import config
 
def connect_database():
    """Connect to the database using config settings."""
    print(f"Connecting to {config.DB_HOST}:{config.DB_PORT}")
    print(f"Database: {config.DB_NAME}")
    
    if config.DEBUG_MODE:
        print("DEBUG: Connection details logged")
 
def check_login_attempts(attempts):
    """Check if login attempts exceed the limit."""
    if attempts >= config.MAX_LOGIN_ATTEMPTS:
        print(f"Too many attempts! Maximum is {config.MAX_LOGIN_ATTEMPTS}")
        return False
    return True
 
connect_database()
print(check_login_attempts(2))  # Output: True
print(check_login_attempts(4))  # Output: Too many attempts! Maximum is 3

Output:

Connecting to localhost:5432
Database: myapp
True
Too many attempts! Maximum is 3

Wichtig: Variablen auf Modul-Ebene werden über alle Importe hinweg geteilt. Wenn Sie eine Modulvariable ändern, wirkt sich diese Änderung auf allen Code aus, der dieses Modul verwendet:

python
# file1.py
import config
config.DEBUG_MODE = True
print(f"File1 - Debug mode: {config.DEBUG_MODE}")
 
# file2.py
import config
print(f"File2 - Debug mode: {config.DEBUG_MODE}")  # Output: Will be True!

Dieses Verhalten kann nützlich sein, aber auch überraschend. Seien Sie vorsichtig, wenn Sie Variablen auf Modul-Ebene ändern.

Private Namen in Modulen

Konventionsgemäß gelten Namen, die mit einem Unterstrich beginnen, als privat oder intern für das Modul:

python
# user_manager.py
"""Module for managing user accounts."""
 
# Private helper function
def _validate_email(email):
    """Internal function to validate email format."""
    return '@' in email and '.' in email
 
# Public function
def create_user(username, email):
    """Create a new user account."""
    if not _validate_email(email):
        return None
    
    user = {
        'username': username,
        'email': email,
        'active': True
    }
    return user
 
# Private constant
_MAX_USERNAME_LENGTH = 20
 
# Public constant
MIN_PASSWORD_LENGTH = 8

Wenn Sie from user_manager import * verwenden, werden private Namen (die mit Unterstrich beginnen) nicht importiert. Sie können jedoch weiterhin explizit darauf zugreifen, falls nötig:

python
import user_manager
 
# Öffentliche Funktion – zur Verwendung vorgesehen
user = user_manager.create_user("alice", "alice@example.com")
 
# Private Funktion – ist zugreifbar, aber Sie sollten sich nicht darauf verlassen
# (sie könnte sich in zukünftigen Versionen ändern)
is_valid = user_manager._validate_email("test@test.com")

Das Unterstrich-Präfix ist ein Signal an andere Programmiererinnen und Programmierer: „Das ist ein Implementierungsdetail. Verlassen Sie sich nicht darauf, dass es gleich bleibt.“

22.5) Pakete verstehen und __init__.py

Wenn Projekte wachsen, möchten Sie mehrere zusammengehörige Module in einem Paket organisieren. Ein Paket ist ein Verzeichnis, das Python-Module und eine spezielle Datei __init__.py enthält.

Was ist ein Paket?

Ein Paket ist eine Möglichkeit, mehrere Module in einer hierarchischen Struktur zu organisieren. Denken Sie daran wie an einen Ordner, der Python-Dateien enthält, wobei der Ordner selbst importiert werden kann.

Hier ist eine einfache Paketstruktur:

myproject/
    main.py
    utilities/
        __init__.py
        text.py
        math.py
        file.py

In dieser Struktur ist utilities ein Paket, das drei Module enthält: text, math und file. Die Datei __init__.py (die leer sein kann) sagt Python, dass utilities ein Paket ist.

Ein einfaches Paket erstellen

Erstellen wir ein Paket für Datenverarbeitung. Erstellen Sie zuerst diese Verzeichnisstruktur:

data_tools/
    __init__.py
    validators.py
    formatters.py

Erstellen Sie validators.py:

python
# data_tools/validators.py
"""Data validation functions."""
 
def is_valid_email(email):
    """Check if email has basic valid format."""
    return '@' in email and '.' in email.split('@')[1]
 
def is_valid_phone(phone):
    """Check if phone number has valid format (simple check)."""
    digits = ''.join(c for c in phone if c.isdigit())
    return len(digits) == 10
 
def is_positive_number(value):
    """Check if value is a positive number."""
    try:
        return float(value) > 0
    except (ValueError, TypeError):
        return False

Erstellen Sie formatters.py:

python
# data_tools/formatters.py
"""Data formatting functions."""
 
def format_phone(phone):
    """Format phone number as (XXX) XXX-XXXX."""
    digits = ''.join(c for c in phone if c.isdigit())
    if len(digits) != 10:
        return phone
    return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
 
def format_currency(amount):
    """Format number as currency."""
    return f"${amount:,.2f}"
 
def format_percentage(value, decimals=1):
    """Format number as percentage."""
    return f"{value * 100:.{decimals}f}%"

Erstellen Sie eine leere __init__.py:

python
# data_tools/__init__.py
"""Data processing tools package."""

Aus Paketen importieren

Jetzt können Sie auf mehrere Arten aus dem Paket importieren:

python
# Methode 1: Das Modul aus dem Paket importieren
import data_tools.validators
 
email = "user@example.com"
is_valid = data_tools.validators.is_valid_email(email)
print(f"Email valid: {is_valid}")  # Output: Email valid: True
 
# Methode 2: Bestimmtes Modul mit from importieren
from data_tools import formatters
 
phone = "1234567890"
formatted = formatters.format_phone(phone)
print(f"Formatted phone: {formatted}")  # Output: Formatted phone: (123) 456-7890
 
# Methode 3: Bestimmte Funktionen importieren
from data_tools.validators import is_valid_phone
from data_tools.formatters import format_currency
 
print(is_valid_phone("555-1234"))  # Output: False (not 10 digits)
print(format_currency(1234.56))    # Output: $1,234.56

Die Datei __init__.py

Die Datei __init__.py erfüllt zwei Zwecke:

  1. Markiert das Verzeichnis als Paket: Python erkennt Verzeichnisse mit __init__.py als Pakete
  2. Initialisierungscode für das Paket: Code in __init__.py läuft, wenn das Paket zum ersten Mal importiert wird

Die Datei __init__.py kann leer sein, sie kann aber auch Code enthalten, um das Paket leichter nutzbar zu machen:

python
# data_tools/__init__.py
"""Data processing tools package."""
 
# Import commonly used functions into package namespace
from data_tools.validators import is_valid_email, is_valid_phone
from data_tools.formatters import format_phone, format_currency
 
# Package version
__version__ = '1.0.0'
 
# Package-level constant
DEFAULT_CURRENCY_SYMBOL = '$'

Jetzt können Nutzerinnen und Nutzer direkt aus dem Paket importieren:

python
# Instead of: from data_tools.validators import is_valid_email
# You can write:
from data_tools import is_valid_email, format_currency
 
print(is_valid_email("test@test.com"))  # Output: True
print(format_currency(99.99))           # Output: $99.99

22.6) Die Variable __name__ und if __name__ == "__main__": Eine Datei als Skript ausführen

Python-Dateien können zwei Zwecke erfüllen: Sie können als Module importiert oder als eigenständige Skripte ausgeführt werden. Die spezielle Variable __name__ hilft Ihnen dabei, Code zu schreiben, der in beiden Situationen gut funktioniert.

__name__ verstehen

Jedes Python-Modul hat eine eingebaute Variable namens __name__. Python setzt diese Variable unterschiedlich, je nachdem, wie die Datei verwendet wird:

  • Beim Import: __name__ wird auf den Namen des Moduls gesetzt
  • Beim direkten Ausführen: __name__ wird auf "__main__" gesetzt

Sehen wir uns das in Aktion an. Erstellen Sie eine Datei namens demo_name.py:

python
# demo_name.py
print(f"The __name__ variable is: {__name__}")

Führen Sie sie nun direkt aus:

bash
python demo_name.py

Output:

The __name__ variable is: __main__

Importieren Sie sie nun aus einer anderen Datei:

python
# test_import.py
import demo_name

Output:

The __name__ variable is: demo_name

Wenn Sie demo_name.py direkt ausführen, setzt Python __name__ auf "__main__". Wenn Sie es importieren, setzt Python __name__ auf den Modulnamen ("demo_name").

Das Pattern if __name__ == "__main__":

Dieses Verhalten ermöglicht Ihnen, Code zu schreiben, der nur ausgeführt wird, wenn die Datei direkt ausgeführt wird, nicht wenn sie importiert wird. Das geschieht mit dem Pattern:

python
if __name__ == "__main__":
    # Code here runs only when file is executed directly
    pass

Hier ist, warum das nützlich ist. Erstellen Sie math_utils.py:

python
# math_utils.py
"""Utility functions for mathematical operations."""
 
def calculate_area(radius):
    """Calculate the area of a circle."""
    return 3.14159 * radius ** 2
 
def calculate_circumference(radius):
    """Calculate the circumference of a circle."""
    return 2 * 3.14159 * radius
 
# Test code - runs only when file is executed directly
if __name__ == "__main__":
    print("Testing math_utils functions...")
    
    test_radius = 5
    area = calculate_area(test_radius)
    circumference = calculate_circumference(test_radius)
    
    print(f"Radius: {test_radius}")
    print(f"Area: {area:.2f}")
    print(f"Circumference: {circumference:.2f}")

Wenn Sie diese Datei direkt ausführen:

bash
python math_utils.py

Output:

Testing math_utils functions...
Radius: 5
Area: 78.54
Circumference: 31.42

Aber wenn Sie sie importieren:

python
# use_math_utils.py
import math_utils
# The test code doesn't run!
 
area = math_utils.calculate_area(10)
print(f"Area of circle: {area:.2f}")  # Output: Area of circle: 314.16

Der Testcode im if __name__ == "__main__":-Block wird beim Import nicht ausgeführt. Dadurch können Sie Testcode, Beispiele oder Demonstrationen in Ihren Modulen einbauen, ohne Code zu beeinflussen, der sie importiert.

Häufige Verwendungen von if __name__ == "__main__":

Tests und Demonstrationen

Binden Sie Beispiele ein, die zeigen, wie Sie Ihr Modul verwenden:

python
# string_tools.py
def reverse_string(text):
    """Reverse a string."""
    return text[::-1]
 
def count_vowels(text):
    """Count vowels in text."""
    vowels = 'aeiouAEIOU'
    return sum(1 for char in text if char in vowels)
 
if __name__ == "__main__":
    # Demonstration code
    sample = "Hello, World!"
    
    print(f"Original: {sample}")
    print(f"Reversed: {reverse_string(sample)}")
    print(f"Vowels: {count_vowels(sample)}")

In diesem Kapitel haben wir gelernt, wie man Python-Code mit Modulen und Paketen organisiert. Wir haben untersucht, wie das Importsystem funktioniert, verschiedene Wege, Code zu importieren, und wie wir unsere eigenen Module und Pakete erstellen. Wir haben außerdem die Variable __name__ und das Pattern if __name__ == "__main__": kennengelernt, mit dem Dateien sowohl als importierbare Module als auch als eigenständige Skripte funktionieren können.

Diese Organisationswerkzeuge werden immer wichtiger, je größer Ihre Programme werden. Im nächsten Kapitel werden wir untersuchen, wie man Funktionen als Daten verwendet und einfache funktionale Programmiertechniken anwendet – aufbauend auf dem soliden Fundament der Codeorganisation, das wir hier gelegt haben.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai