Module (und Pakete) sind eine großartige pythonische Möglichkeit, Ihr Programm in separate Namespaces zu unterteilen, was ein implizites Ziel dieser Frage zu sein scheint. Als ich die Grundlagen von Python lernte, war ich frustriert über das Fehlen einer Block-Scope-Funktion. Sobald ich jedoch die Python-Module verstanden hatte, konnte ich meine vorherigen Ziele eleganter verwirklichen, ohne dass ein Blockumfang erforderlich war.
Als Motivation und um die Menschen in die richtige Richtung zu weisen, halte ich es für nützlich, explizite Beispiele für einige der Scoping-Konstrukte von Python zu geben. Zuerst erkläre ich meinen fehlgeschlagenen Versuch, Python-Klassen zum Implementieren des Blockbereichs zu verwenden. Als nächstes erkläre ich, wie ich mit Python-Modulen etwas Nützlicheres erreicht habe. Am Ende skizziere ich eine praktische Anwendung von Paketen zum Laden und Filtern von Daten.
Blockbereich mit Klassen versuchen
Für einige Momente dachte ich, ich hätte den Blockumfang erreicht, indem ich Code in eine Klassendeklaration gesteckt habe:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
print(x) # Output: 5
Leider bricht dies zusammen, wenn eine Funktion definiert wird:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(x)
printx2() # Output: 5!!!
Dies liegt daran, dass innerhalb einer Klasse definierte Funktionen einen globalen Bereich verwenden. Der einfachste (wenn auch nicht der einzige) Weg, dies zu beheben, besteht darin, die Klasse explizit anzugeben:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(BlockScopeAttempt.x) # Added class name
printx2() # Output: 10
Dies ist nicht so elegant, da man Funktionen unterschiedlich schreiben muss, je nachdem, ob sie in einer Klasse enthalten sind oder nicht.
Bessere Ergebnisse mit Python-Modulen
Module sind statischen Klassen sehr ähnlich, aber Module sind meiner Erfahrung nach viel sauberer. Um dasselbe mit Modulen zu tun, erstelle ich eine my_module.py
im aktuellen Arbeitsverzeichnis aufgerufene Datei mit folgendem Inhalt:
x = 10
print(x) # (A)
def printx():
global x
print(x) # (B)
Dann mache ich es in meiner Hauptdatei oder in einer interaktiven Sitzung (z. B. Jupyter)
x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5
Zur Erklärung definiert jede Python-Datei ein Modul mit einem eigenen globalen Namespace. Durch das Importieren eines Moduls können Sie mit der .
Syntax auf die Variablen in diesem Namespace zugreifen .
Wenn Sie in einer interaktiven Sitzung mit Modulen arbeiten, können Sie diese beiden Zeilen zu Beginn ausführen
%load_ext autoreload
%autoreload 2
und Module werden automatisch neu geladen, wenn die entsprechenden Dateien geändert werden.
Pakete zum Laden und Filtern von Daten
Die Idee der Pakete ist eine leichte Erweiterung des Modulkonzepts. Ein Paket ist ein Verzeichnis mit einer (möglicherweise leeren) __init__.py
Datei, die beim Import ausgeführt wird. Auf Module / Pakete in diesem Verzeichnis kann mit der .
Syntax zugegriffen werden .
Für die Datenanalyse muss ich häufig eine große Datendatei lesen und dann interaktiv verschiedene Filter anwenden. Das Lesen einer Datei dauert einige Minuten, daher möchte ich es nur einmal tun. Basierend auf dem, was ich in der Schule über objektorientiertes Programmieren gelernt habe, war ich der Meinung, dass man den Code zum Filtern und Laden als Methoden in einer Klasse schreiben sollte. Ein Hauptnachteil dieses Ansatzes besteht darin, dass sich die Definition meiner Klasse ändert, wenn ich meine Filter neu definiere, sodass ich die gesamte Klasse einschließlich der Daten neu laden muss.
Heutzutage definiere ich mit Python ein Paket namens, my_data
das Submodule mit dem Namen load
und enthält filter
. Innerhalb von filter.py
kann ich einen relativen Import durchführen:
from .load import raw_data
Wenn ich ändere filter.py
, autoreload
werden die Änderungen erkannt. Es wird nicht neu geladen load.py
, daher muss ich meine Daten nicht neu laden. Auf diese Weise kann ich meinen Filtercode in einem Jupyter-Notizbuch prototypisieren, als Funktion umschließen und dann direkt aus meinem Notizbuch ausschneiden und einfügen filter.py
. Das herauszufinden hat meinen Workflow revolutioniert und mich von einem Skeptiker zu einem Gläubigen des „Zen of Python“ gemacht.
One purpose (of many) is to improve code readability
- Richtig geschriebener Python-Code (dh nach dem Zen von Python ) würde eine solche Verzierung nicht benötigen, um lesbar zu sein. Tatsächlich ist es eines der (vielen) Dinge, die ich an Python mag.