9 Globals und Locals in depth

9.1 Einführung in Globals und Locals

Es gibt zwei Haupttypen von Variablen: Globale und lokale Variablen. Das Verständnis der Unterschiede zwischen diesen Typen und ihrer jeweiligen Verwendung ist entscheidend für das Schreiben von gut strukturiertem, wartbarem und effizientem Code.

Insgesamt sollte man, wann immer möglich, lokale Variablen verwenden, um den Code wartbarer, lesbarer und weniger fehleranfällig zu gestalten. Globale Variablen sollten mit Bedacht verwendet werden, insbesondere in größeren Projekten, um unerwünschte Seiteneffekte und schwer zu findende Fehler zu vermeiden.

9.2 Lokaler Scope und Funktionen

Der lokale Scope bezieht sich auf den Bereich innerhalb einer Funktion oder Methode, in dem lokale Variablen definiert und verwendet werden können. Das Verständnis des lokalen Scopes ist entscheidend für das Schreiben von gut strukturiertem und effizientem Code.

def outer_function():
    outer_var = 10

    def inner_function():
        inner_var = 5
        print("Innerhalb von inner_function:", outer_var, inner_var)

    inner_function()
    print("Innerhalb von outer_function:", outer_var)

9.3 Globaler Scope und Module

Der globale Scope ist der Bereich eines Python-Skripts oder Moduls, in dem globale Variablen definiert und verwendet werden können. Das Verständnis des globalen Scopes ist wichtig, um Code korrekt zu strukturieren und unerwünschte Seiteneffekte zu vermeiden.

# module1.py
global_var = 42

# module2.py
import module1

print(module1.global_var)  # Output: 42

Verwendung des ‘global’ Schlüsselworts: Das ‘global’ Schlüsselwort wird verwendet, um innerhalb einer Funktion auf eine globale Variable zu verweisen und sie zu ändern. Ohne das ‘global’ Schlüsselwort würde Python eine lokale Variable erstellen, die den gleichen Namen wie die globale Variable trägt, anstatt die globale Variable zu ändern:

global_var = 10

def change_global_var():
    global global_var
    global_var = 42

print(global_var)  # Output: 10
change_global_var()
print(global_var)  # Output: 42

9.4 Zugriff auf globale und lokale Variablen

Sie können die eingebauten Funktionen globals() und locals() verwenden, um auf den globalen und lokalen Scope zuzugreifen.

Die eingebauten Funktionen ‘globals()’ und ‘locals()’:

def example_function():
    local_var = "Hello, World!"
    print("Local Scope:", locals())

example_function()
print("Global Scope:", globals())

9.5 Funktionsparameter und Argumente

Sie können verschiedene Arten von Argumenten verwenden um Daten an Funktionen zu übergeben. Das Verständnis dieser verschiedenen Argumenttypen ist entscheidend für das Schreiben flexibler und effizienter Funktionen.

Positionale und Schlüsselwort-Argumente:

def greet(name, greeting):
    print(greeting, name)

greet("Alice", "Hello")  # Positionale Argumente
greet(name="Bob", greeting="Hi")  # Schlüsselwort-Argumente
greet(greeting="Hey", name="Carol")  # Schlüsselwort-Argumente in anderer Reihenfolge

Default-Parameter und Mutable-Objekte:

Default-Parameter sind in Python sehr nützlich, um Funktionen flexibler und einfacher zu verwenden. Sie stellen Standardwerte für Funktionsparameter bereit, wenn keine Werte beim Funktionsaufruf übergeben werden. Es gibt jedoch einige Fallstricke, insbesondere wenn mutable Objekte wie Listen oder Wörterbücher als Default-Werte verwendet werden.

Verwendung von Default-Parametern:

def greet(name, greeting="Hello"):
    print(greeting, name)

greet("Alice")  # Output: Hello Alice (greeting hat den Default-Wert "Hello")
greet("Bob", "Hi")  # Output: Hi Bob (greeting wird durch den übergebenen Wert "Hi" ersetzt)

Mutable Objekte als Default-Werte:

Bei Verwendung von mutable Objekten wie Listen oder Wörterbüchern als Default-Werte kann es zu unerwarteten Verhaltensweisen kommen, da der Default-Wert für alle Funktionsaufrufe gemeinsam genutzt wird.

def add_item(item, items_list=[]):
    items_list.append(item)
    return items_list

print(add_item(1))  # Output: [1]
print(add_item(2))  # Output: [1, 2] (anstelle von [2], wie vielleicht erwartet)

Um dieses Problem zu vermeiden, können Sie None als Default-Wert verwenden und innerhalb der Funktion eine neue Liste oder ein neues Wörterbuch erstellen, wenn der Parameterwert None ist.

def add_item(item, items_list=None):
    if items_list is None:
        items_list = []
    items_list.append(item)
    return items_list

print(add_item(1))  # Output: [1]
print(add_item(2))  # Output: [2] (wie erwartet)

Durch die Verwendung von None als Default-Wert für den Parameter items_list wird sichergestellt, dass für jeden Funktionsaufruf eine neue Liste erstellt wird, wenn keine Liste übergeben wird. Dies verhindert unerwartete Seiteneffekte und macht den Code sicherer und einfacher zu verstehen.

9.6 Closures und Lexikalische Umgebung

Closures und Lexikalische Umgebung

Closures sind ein leistungsfähiges Konzept in der funktionalen Programmierung und ermöglichen das Festhalten von Werten aus dem umgebenden Scope. Sie sind eng mit der lexikalischen Umgebung verknüpft, die den Zugriff auf Variablen außerhalb des lokalen Scopes einer Funktion ermöglicht.

Definition von Closures: Ein Closure ist eine Funktion, die einen Bezug zu einer oder mehreren Variablen aus ihrem umgebenden Scope besitzt, selbst wenn der äußere Scope nicht mehr aktiv ist. Closures ermöglichen es, den Zustand über mehrere Funktionsaufrufe hinweg beizubehalten, ohne globale Variablen oder Klassen verwenden zu müssen.

Lexikalische Umgebung und Variablenzugriff: Die lexikalische Umgebung bezieht sich auf den Bereich, in dem eine Funktion definiert wurde, und schließt alle Variablen ein, die im umgebenden Scope verfügbar sind. In Python ermöglicht die lexikalische Umgebung den Zugriff auf äußere Variablen, die im umgebenden Scope einer verschachtelten Funktion definiert sind. Dieser Zugriff ist jedoch standardmäßig schreibgeschützt, es sei denn, das Schlüsselwort nonlocal wird verwendet.

Anwendungsfälle und Beispiele für Closures:

def make_counter():
    count = 0

    def counter():
        nonlocal count
        count += 1
        return count

    return counter

my_counter = make_counter()
print(my_counter())  # Output: 1
print(my_counter())  # Output: 2
def make_multiplier(factor):
    def multiplier(value):
        return value * factor

    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))  # Output: 10
print(triple(5))  # Output: 15

In beiden Beispielen verwenden die inneren Funktionen (counter und multiplier) Variablen aus ihrem umgebenden Scope (count und factor), wodurch Closures entstehen. Die äußeren Funktionen (make_counter und make_multiplier) dienen als Fabriken, um diese Closures mit dem gewünschten Anfangszustand oder der gewünschten Konfiguration zu erstellen.