Skip to content

9. Importe

Der in Version 1 der Anwendungsübung aufgetretene Fehler veranlasst uns, die Rolle der [import]-Anweisung genauer zu untersuchen.

Image

9.1. Skripte [import_01]

Das Skript [imported] wird von verschiedenen Skripten (auch Module genannt) importiert:

Image

1
2
3
4
5
#  imported module
#  this instruction will be executed each time the module is imported
print("2")
#  variable belonging to the imported module
x=4

Ein Modul wird ausgeführt, wenn es importiert wird. Wenn also das [importierte] Modul importiert wird:

  • wird die Ausgabe von Zeile 3 erzeugt;
  • wird der Variablen x in Zeile 5 ihr Wert zugewiesen;

Das Skript [main_01] lautet wie folgt:

1
2
3
4
#  an imported module is executed
import imported
#  use of the x variable of the imported module
print(imported.x)
  • Zeile 2: Das Modul [imported] wird importiert. Dadurch wird es ausgeführt:
    • Der Wert 2 wird angezeigt;
    • die Variable x wird mit dem Wert 4 angelegt;
  • Zeile 4: Die Variable x aus dem importierten Modul wird verwendet;

In PyCharm wird ein Fehler gemeldet:

In [1] zeigt PyCharm an, dass es das [importierte] Modul nicht erkennt. Technisch gesehen bedeutet dies, dass sich der Ordner, der das [importierte] Modul enthält, nicht im Python-Pfad von PyCharm befindet. Der Python-Pfad ist die Menge der Ordner, in denen nach importierten Modulen gesucht wird. Um dieses Problem zu beheben, legen Sie einfach den Ordner, der das [importierte] Modul enthält, als [Root Sources]-Ordner fest, in diesem Fall den Ordner [import/01]:

Image

Image

Nach diesem Schritt wird der Ordner [import/01] zum Python-Pfad von PyCharm hinzugefügt und der Fehler verschwindet:

Image

  • in [1] hat der Ordner [01] seine Farbe geändert;
  • in [2-3] wird kein Fehler mehr angezeigt;

Die Ausführungsergebnisse lauten wie folgt:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_01.py
2
4

Process finished with exit code 0

Kommentare

  • Zeile 2 ist das Ergebnis der Ausführung des importierten Moduls;
  • Zeile 3 zeigt den Wert der Variablen x aus dem importierten Modul an;

Die wichtigste Erkenntnis aus diesem Beispiel ist das wichtige Konzept, dass ein importiertes Modul (oder Skript) ausgeführt wird.

Das Skript [main_02] lautet wie folgt:

1
2
3
4
#  import the x variable from the imported module
from imported import x
#  we display it
print(x)
  • Zeile 2 verwendet eine andere Import-Syntax [from module import object1, object2, …]. Hier importieren wir die Variable [imported.x]. Mit dieser Syntax wird die Variable x zu einer Variablen des Skripts [main_02]. Wir müssen ihr nicht mehr das Modul [imported] voranstellen;
  • Zeile 4: Wir geben die Variable x aus [main_02] aus;

Die Ergebnisse der Ausführung lauten wie folgt:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_02.py
2
4

Process finished with exit code 0

Das Skript [main_03] lautet wie folgt:

1
2
3
4
#  import everything visible in the imported module
from imported import *
#  use the x variable of the imported module
print(x)

Die Notation [import *] in Zeile 2 bedeutet, dass wir alle sichtbaren Objekte aus dem importierten Modul importieren (Variablen, Funktionen).

Die Ergebnisse lauten wie folgt:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_03.py
2
4

Process finished with exit code 0

Das Skript [main_04] lautet wie folgt:

1
2
3
4
5
#  import the x variable from the imported module
#  and rename it y
from imported import x as y
#  display variable y
print(y) 

Zeile 3 zeigt, dass wir ein Objekt aus dem Modul „imported“ importieren und ihm einen Alias zuweisen können. Hier wird die Variable [imported.x] zur Variable [main_04.y]. Die Ergebnisse sind dieselben wie zuvor.

9.2. Skript [import_02]

Image

Das importierte Modul [module1.py] sieht wie folgt aus:

1
2
3
#  a function
def f1():
    print("f1")

Das importierte Modul definiert eine Funktion, was ein gängiges Szenario ist.

Das Skript [main_01] lautet wie folgt:

1
2
3
4
#  import
import module1
#  execution f1
module1.f1()
  • Zeile 2: Das Modul wird importiert. Es wird ausgeführt. Hier wird nichts angezeigt;
  • Zeile 4: Die Funktion [f1] aus dem importierten Modul wird ausgeführt;

Die Ergebnisse der Ausführung lauten wie folgt:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/02/main_01.py
f1

Process finished with exit code 0

Hinweis: Damit PyCharm keinen Fehler beim Import in Zeile 2 meldet, müssen Sie den Ordner, der [module1] enthält, in den [Stammverzeichnissen] von PyCharm ablegen:

Image

In [1] wird der Ordner [02] im Verzeichnis [Root Sources] blau angezeigt. Beachten Sie, dass der gemeldete Fehler die korrekte Ausführung der Skripte hier nicht verhindert. Tatsächlich wird beim Ausführen des Skripts [main_0x] der Skriptordner automatisch zum Python-Pfad hinzugefügt. Dadurch wird [module1] gefunden. Wenn ein Ordner auf einem Screenshot von nun an blau dargestellt wird, bedeutet dies, dass er in PyCharms [Root Sources] abgelegt wurde.

Das Skript [main_02] lautet wie folgt:

1
2
3
4
#  import
from module1 import f1
#  execution f1
f1()
  • In Zeile 2 wird die Funktion [f1] aus dem Modul [module1] importiert;
  • In Zeile 4 wird die Funktion f1 verwendet;

Die Ergebnisse sind identisch mit denen des Skripts [main_01].

9.3. Skripte [import_03]

Image

Hinweis: [03] befindet sich im Projektordner [Root Sources].

Die neuen Skripte importieren das Modul [module2], das sich nicht im selben Ordner wie sie befindet.

Das Skript [module2] lautet wie folgt:

1
2
3
#  a function
def f2():
    print("f2")

Das Skript definiert somit eine Funktion [f2].

Das Skript [main_01] lautet wie folgt:

1
2
3
4
#  class2 module import
import dir1.module2
#  execution f2
dir1.module2.f2()
  • Zeile 2: Wir verwenden eine spezielle Notation, um anzugeben, wie das Modul [module2] zu finden ist. [dir1.module2] ist als Pfad [dir1/module2] zu lesen: Um [module2] zu finden, beginnst du im Ordner des aktuellen Skripts [main_01], wechselst dann in [dir1] und findest dort [module2]. Beachten Sie, dass der Startpunkt des Pfades der Ordner des Skripts ist, das importiert;
  • Zeile 4: Um die Funktion [f2] aus [module2] auszuführen;

Die Ergebnisse lauten wie folgt:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/03/main_01.py
f2

Process finished with exit code 0

Zeile 2, das Ergebnis der Funktion [f2].

Das Skript [main_02] lautet wie folgt:

1
2
3
4
#  import module dir1.module2 and rename it
import dir1.module2 as module2
#  execution f2
module2.f2()

In Zeile 2 benennen wir das Modul [dir1.module] um, um den Code in Zeile 4 zu vereinfachen.

Das Skript [main_03] lautet wie folgt:

1
2
3
4
#  import function f2 from module dir1.module2
from dir1.module2 import f2
#  execution f2
f2()

Diesmal importieren wir in Zeile 2 nur die Funktion [f2], die dann zu einer Funktion des Skripts [main_03] wird (Zeile 4).

Alle diese Skripte funktionieren in der PyCharm-Umgebung genauso gut wie in einer Python-Konsole. Der Grund dafür ist, dass in beiden Fällen das Verzeichnis des ausgeführten Skripts – hier das Verzeichnis [03] – Teil des Python-Pfads ist. Dadurch wird das Verzeichnis [dir1/module2] gefunden.

9.4. Skripte [import_04]

Image

Hier wurden die Ordner [dir1] und [dir2] im Verzeichnis [Root Sources] des PyCharm-Projekts abgelegt.

Das erste importierte Modul ist [module3]:

1
2
3
#  a function
def f3():
    print("f3")

Das zweite importierte Modul ist [module4]:

1
2
3
4
5
6
from module3 import f3

#  a function
def f4():
    f3()
    print("f4")
  • Zeile 1: Wir importieren die Funktion [f3] aus [module3]. Hier ist [module3] sichtbar, da wir dessen Verzeichnis [dir1] in den [Root Sources] abgelegt haben;
  • Zeilen 4–6: Wir definieren eine Funktion [f4], die die Funktion [f3] aus [module3] aufruft;

Das Hauptskript [main_01] lautet wie folgt:

1
2
3
4
#  import module4
from module4 import f4
#  execution f4
f4()
  • Zeile 2: Importiere das Modul [module4]. Dies ist sichtbar, da wir dessen Verzeichnis [dir2] in PyCharms [Root Sources] abgelegt haben;
  • Zeile 4: Ausführung der Funktion [f4] aus [module4];

Die Ergebnisse der Ausführung von [main_01] in PyCharm lauten wie folgt:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/04/main_01.py
f3
f4

Process finished with exit code 0

Führen wir nun [main_01] in einem Python-Terminal (Konsole) aus:

Image

Die Ergebnisse lauten wie folgt:

1
2
3
4
5
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\04>python main_01.py
Traceback (most recent call last):
  File "main_01.py", line 2, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'

Was ist passiert? Das Python-Terminal kennt weder den Python-Pfad von PyCharm noch die [Root Sources]. Es verfügt über einen eigenen Python-Pfad. In diesem Pfad befindet sich immer der Ordner des gerade ausgeführten Skripts, in diesem Fall das Skript [main_01]. Es kennt daher den Ordner [import/04]. Im ausgeführten Skript findet es die Zeile:


from module4 import f4

Der Python-Interpreter sucht in den Ordnern seines Python-Pfads nach [module4]. [module4] befindet sich jedoch nicht in [import/04] – das zwar im Python-Pfad enthalten ist –, sondern in [import/04/dir2], das nicht im Python-Pfad enthalten ist. Daher der Fehler.

Wir stehen also vor einem Problem, das wir bereits kennen: Ein Skript, das in PyCharm korrekt läuft, kann in einem Python-Terminal abstürzen. Dies ist ein wiederkehrendes Problem, das wir lösen müssen.

9.5. Skripte [import_05]

Image

Hinweis: Die Ordner [dir1] und [dir2] werden dem Python-Pfad hinzugefügt. Beachten Sie, dass hier bereits ein Konflikt besteht: [module3] und [module4] befinden sich an zwei Stellen im Python-Pfad von PyCharm:

  • in [import/04/dir1] und [import/05/dir1] für [module3];
  • in [import/04/dir2] und [import/05/dir2] für [module4];

Anschließend können wir [import/04/dir1] und [import/04/dir2] aus den [Root Sources] des PyCharm-Projekts entfernen. Es stellt sich heraus, dass [import/05/dir1] hier eine Kopie von [import/04/dir1] ist (das Gleiche gilt für [dir2]), sodass es kein Problem gibt. Es ist jedoch zu beachten, dass man innerhalb von PyCharm selbst auf die Liste der Ordner in den [Root Sources] achten muss, um Konflikte zu vermeiden.

Das Skript [main_01] sieht nun wie folgt aus:

import sys
#  modify sys.path to include folders
#  containing the classes to be imported
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
#  import module4
from module4 import f4
#  execution f4
f4()

Wir versuchen, das Problem mit dem Python-Pfad zu lösen. Wir möchten eine Lösung, die in PyCharm genauso gut funktioniert wie in einem Python-Terminal. Dazu richten wir sie selbst ein.

  • Zeilen 4–6: Wir fügen die Verzeichnisse [., ./dir1, ./dir2] zum Python-Pfad hinzu. Damit dies funktioniert, muss das aktuelle Verzeichnis zur Laufzeit das Verzeichnis [import/05] sein. Dies trifft in PyCharm zu, aber nicht unbedingt in einem Python-Terminal, wie wir sehen werden;
  • Zeile 8: Wir importieren [module4]. Basierend auf dem, was wir gerade getan haben, sollte es in [./dir2] zu finden sein;

Die Ausführung in PyCharm liefert folgende Ergebnisse:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/main_01.py
f3
f4

Process finished with exit code 0

Nun in einem Python-Terminal:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4

Zeile 1, das Ausführungsverzeichnis ist [import/05].

Gehen wir nun in der Verzeichnisstruktur eine Ebene von [import/05] nach oben:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>cd ..
 
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_01.py
Traceback (most recent call last):
  File "05/main_01.py", line 8, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'
  • Zeile 2: Wenn [main_01] ausgeführt wird, befinden wir uns nicht mehr im Ordner [import/05], sondern in [import]. Wir haben jedoch geschrieben:

sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")

Dadurch werden die Ordner [import, import/dir1, import/dir2] zum Python-Pfad hinzugefügt, was wir aber überhaupt nicht wollen. Beachten Sie, dass das Hinzufügen von Ordnern, die nicht existieren (import/dir1, import/dir2), zum Python-Pfad keine Fehler verursacht.

Wir haben Fortschritte gemacht, aber das reicht noch nicht. Wir müssen dem Python-Pfad absolute Pfade hinzufügen, keine relativen.

Das Skript [main_02] ist eine Variante von [main_01], die eine Konfigurationsdatei [config.json] verwendet:


{
  "dependencies": [
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir1",
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir2"
  ]
}

Der Wert des Schlüssels [dependencies] ist die Liste der Verzeichnisse, die dem Python-Pfad hinzugefügt werden sollen. Beachten Sie, dass wir hier absolute Pfade anstelle von relativen Pfaden verwendet haben.

Das Skript [main_02] verwendet die Datei [config.json] wie folgt:

import codecs
import json
import sys

#  configuration file
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
#  reading the json configuration file
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

#  modification of sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

#  import module4
from module4 import f4
#  execution f4
f4()
  • Zeile 6: Beachten Sie, dass wir den absoluten Pfad zur Konfigurationsdatei verwendet haben;
  • Zeilen 8–9: Die Konfigurationsdatei wird gelesen. Aus ihrem Inhalt wird ein Wörterbuch [config] (Zeile 9) erstellt;
  • Zeilen 11–13: Die Elemente des Arrays [config['dependencies']] werden dem Python-Pfad hinzugefügt. Beachten Sie, dass wir, da wir in [config.json] absolute Ordnernamen verwendet haben, dem Python-Pfad absolute Namen hinzufügen;
  • Zeile 16: [module4] wird importiert. Es sollte gefunden werden, da sich [dir2] nun im Python-Pfad befindet;

Die Ausführung liefert dieselben Ergebnisse wie bei [main_02], mit dem Unterschied, dass das Skript weiterläuft, auch wenn das Ausführungsverzeichnis nicht mehr [import/05] ist:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4

Zeile 1, das Ausführungsverzeichnis ist [import].

Wir sind einen Schritt weitergekommen. Wir haben gesehen:

  • dass wir den Python-Pfad selbst erstellen mussten;
  • dass wir die absoluten Pfade aller Ordner angeben mussten, die die von der Anwendung importierten Module enthalten;

Allerdings ist das Einfügen absoluter Pfade in Skripte keine Lösung. Sobald das Projekt an einen anderen Standort verschoben wird, funktioniert es nicht mehr. Wir müssen einen anderen Weg finden.

9.6. Skripte [import_06]

Image

Hinweis: Die Ordner [06, dir1, dir2] wurden im Verzeichnis [Root Sources] des PyCharm-Projekts abgelegt. Die Ordner [dir1, dir2] entsprechen denen aus den vorherigen Beispielen.

Die Datei [config.json] sieht wie folgt aus:


{
  "rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
  "relativeDependencies": [
    "dir1",
    "dir2"
  ],
  "absoluteDependencies": [
   ]
}

Wir führen zwei Arten von Pfaden ein:

  • absolute Pfade, Zeilen 7–8;
  • relative Pfade, Zeilen 3–6. Diese beziehen sich auf das Stammverzeichnis in Zeile 2. Wenn das Projekt also an einen neuen Speicherort verschoben wird, muss nur diese Zeile geändert werden;

Das Skript [utils.py] verwendet die Datei [config.json] und erstellt den Python-Pfad:

#  imports
import codecs
import json
import os
import sys

#  application configuration
def config_app(config_filename: str) -> dict:
    #  config_filename: name of configuration file
    #  we let the exceptions rise

    #  using the configuration file
    with codecs.open(config_filename, "r", "utf-8") as file:
        config = json.load(file)
    #  add dependencies to sys.path
    rootDir = config['rootDir']
    #  add the project's relative dependencies to the syspath
    for directory in config['relativeDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, f"{rootDir}/{directory}")
    #  we add the project's absolute dependencies to the syspath
    for directory in config['absoluteDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, directory)
    #  return the configuration dictionary
    return config

#  executed script file
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • Zeile 8: Die Funktion [config_app] erhält den Namen der Konfigurationsdatei als Parameter;
  • Zeilen 12–14: Die Konfigurationsdatei wird verwendet, um das [config]-Wörterbuch zu erstellen;
  • Zeile 20: [sys.path] ist die Liste der Verzeichnisse im Python-Pfad;
  • Zeilen 17–20: Die relativen Abhängigkeiten aus der Konfigurationsdatei werden dem Python-Pfad hinzugefügt. Sie werden am Anfang der Liste [sys.path] hinzugefügt (Zeile 20). Der Grund dafür ist, dass Python bei der Suche nach einem Modul die Verzeichnisse in [sys.path] der Reihe nach durchsucht. In diesem Dokument befinden sich Module mit denselben Namen jedoch in verschiedenen Verzeichnissen innerhalb von [sys.path]. Indem wir die Abhängigkeiten der Anwendung an den Anfang des [sys.path]-Arrays setzen, stellen wir sicher, dass diese vor anderen Verzeichnissen in [sys.path] durchsucht werden, die möglicherweise Module mit denselben Namen enthalten;
  • Zeilen 21–24: Die absoluten Abhängigkeiten der Konfigurationsdatei werden dem Python-Pfad hinzugefügt;
  • Zeile 26: Die Anwendungskonfiguration wird zurückgegeben;
  • Zeilen 29–30: Die Funktion [get_scriptdir] gibt den absoluten Pfad des Verzeichnisses zurück, in dem sich das aktuell ausgeführte Skript befindet (dasjenige, in dem der Funktionsaufruf steht);

Das Hauptskript [main] sieht wie folgt aus:

#  imports
import sys

from utils import config_app


def affiche_path(msg: str):
    #  message
    print(f"{msg}------------------------------")
    #  sys.path
    for path in sys.path:
        print(path)


#  hand -------------
try:
    #  the sys.path is configured
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    #  import module4
    from module4 import f4
    #  execution f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • Zeile 4: Die Funktion [config_app] wird importiert. Beachten Sie, dass dieser [import] immer funktioniert, da sich [utils] und [main] im selben Verzeichnis befinden. Dies liegt daran, dass das Verzeichnis des Hauptskripts automatisch zum Python-Pfad hinzugefügt wird;
  • Zeilen 7–12: Die Funktion [display_path] zeigt die Liste der Ordner im Python-Pfad an;
  • Zeile 19: Die Anwendung wird konfiguriert. Beachten Sie, dass der absolute Pfad der Konfigurationsdatei an die Funktion [config_app] übergeben wird. Nach dieser Anweisung wurde der Python-Pfad neu aufgebaut;
  • Zeile 22: Wir importieren [module4]. Dank der Neuaufbau des Python-Pfads wird dieses Modul gefunden;
  • Zeile 24: Die Funktion [f4] wird ausgeführt;

In der PyCharm-Umgebung lauten die Ausführungsergebnisse wie folgt:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared

C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared
….
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done
 
Process finished with exit code 0

Kommentare

  • Zeilen 2–13: Der Python-Pfad von PyCharm. Er umfasst alle Ordner, die sich im [Root Sources] des Projekts befinden;
  • Zeilen 14–29: der von der Funktion [config_app] erstellte Python-Pfad. Die Zeilen 15–16 enthalten die beiden von uns hinzugefügten Abhängigkeiten;
  • Zeilen 22–27: die Systemverzeichnisse des Python-Interpreters, der das Skript ausgeführt hat;
  • Zeilen 28–29: Die Ausführung verläuft normal;

Kehren wir nun zu dem Kontext zurück, der zuvor einen Laufzeitfehler verursacht hat:

  • Öffnen Sie ein Python-Terminal;
  • Wechseln Sie in ein anderes Verzeichnis als dasjenige, das das ausgeführte Skript enthält;

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

Diesmal funktioniert es (Zeilen 20–21). Beachten Sie, dass [sys.path] nicht dieselben Verzeichnisse enthält wie bei der Ausführung unter PyCharm.

9.7. Skripte [import_07]

Wir verbessern die vorherige Lösung in zweierlei Hinsicht:

  • Wir ersetzen die Konfigurationsdatei [config.json] durch ein Skript [config.py]. Tatsächlich stellt die JSON-Datei ein erhebliches Problem dar: Sie lässt sich nicht mit Kommentaren versehen. Das [config.json]-Wörterbuch kann durch ein Python-Wörterbuch ersetzt werden, das den Vorteil hat, dass es kommentiert werden kann;
  • wir verwenden ein Modul, das für alle Python-Projekte auf dem Rechner sichtbar ist;

9.7.1. Installation eines systemweiten Moduls

Image

Oben erstellen wir einen Ordner [packages/myutils] im PyCharm-Projekt (die Namen spielen keine Rolle).

Das Skript [myutils.py] sieht wie folgt aus:

#  imports
import sys
import os


def set_syspath(absolute_dependencies: list):
    #  absolute_dependencies: a list of absolute folder names

    #  add the project's absolute dependencies to the syspath
    for directory in absolute_dependencies:
        #  we check the existence of the file
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            #  an exception is lifted
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            #  add the folder at the beginning of the syspath
            sys.path.insert(0, directory)
  • Zeilen 6–18: Die Funktion [set_syspath] erstellt einen Python-Pfad mit der Liste der Verzeichnisse, die ihr als Parameter übergeben wurden;
  • Zeilen 12–15: Wir prüfen, ob das Verzeichnis, das zum Python-Pfad hinzugefügt werden soll, existiert;

Das Skript [__init.py__] (zwei Unterstriche vor und nach dem Namen; dies ist erforderlich) lautet wie folgt:


from .myutils import set_syspath

Wir importieren die Funktion [set_syspath] aus dem Skript [myutils]. Die Notation [.myutils] bezieht sich auf den Pfad [./myutils], was bedeutet, dass sich das Skript [myutils] im selben Verzeichnis wie [__init.py] befindet. Wir hätten auch die Notation [myutils] verwenden können. Wir werden jedoch ein systemweites [myutils]-Modul erstellen. Infolgedessen würde die Notation [from myutils import set_syspath] mehrdeutig werden. Bezieht sie sich auf den Import des Skripts [myutils] aus dem aktuellen Verzeichnis oder auf das systemweite Skript [myutils]? Die Notation [.myutils] beseitigt diese Mehrdeutigkeit.

Das Skript [setup.py] (auch hier ist der Name festgelegt) lautet wie folgt:


from setuptools import setup
 
setup(name='myutils',
      version='0.1',
      description='Utilitaire fixant le Python Path',
      url='#',
      author='st',
      author_email='st@gmail.com',
      license='MIT',
      packages=['myutils'],
      zip_safe=False)

In diesem Skript beschreiben wir das Modul, das wir erstellen werden. Hier erstellen wir es lokal. Der gleiche Prozess wird jedoch auch zur Erstellung eines offiziell vertriebenen Moduls verwendet (siehe |pypi|). Die wichtigen Punkte hierbei sind folgende:

  • Zeile 3: der Name des zu erstellenden Moduls;
  • Zeile 4: die Version des Moduls;
  • Zeile 5: seine Beschreibung;
  • Zeilen 7–8: der Autor des Moduls;

Um dieses Modul systemweit zu installieren, gehen Sie wie folgt vor:

Image

Geben Sie dann im Python-Terminal den folgenden Befehl ein [pip install .]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
  Attempting uninstall: myutils
    Found existing installation: myutils 0.1
    Uninstalling myutils-0.1:
      Successfully uninstalled myutils-0.1
    Running setup.py install for myutils ... done
Successfully installed myutils-0.1

Ab sofort kann jedes Skript auf dem Rechner das Modul [myutils] importieren, ohne dass es im Projektcode enthalten ist.

9.7.2. Das Skript [config.py]

Image

Das Skript [config.py] übernimmt die Konfiguration der Anwendung:

def configure():
    import os

    #  absolute name of the configuration script folder
    script_dir = os.path.dirname(os.path.abspath(__file__))
    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        #  local files
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • Zeile 1: Die Funktion [configure] übernimmt die Konfiguration der Anwendung;
  • Zeilen 7–10: das Wörterbuch, das zuvor in [config.json] enthalten war;
  • Zeilen 9–10: Da wir uns in einem Skript befinden, können wir direkt auf die absoluten Ordnernamen [dir1, dir2] zugreifen;
  • Zeilen 12–14: Wir verwenden die Funktion [set_syspath] aus dem soeben erstellten Modul [myutils], um den Python-Pfad für die Konfiguration festzulegen;
  • Zeile 20: Wir geben das Wörterbuch mit der Anwendungskonfiguration zurück. Hier ist es leer;

9.7.3. Das Skript [main.py]

Das Hauptskript [main] sieht wie folgt aus:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from module4 import f4

#  hand -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • Zeilen 2–4: Wir konfigurieren die Anwendung mithilfe des Moduls [config.py]. Darauf kann zugegriffen werden, da es sich im selben Verzeichnis wie das Hauptskript befindet. Das Verzeichnis des Hauptskripts ist jedoch immer Teil des Python-Pfads;
  • wenn wir Zeile 6 erreichen, ist der Python-Pfad so aufgebaut, dass er den Ordner des Moduls [module4] enthält. Wir können es daher in Zeile 7 importieren;
  • Zeilen 10–15: Jetzt muss nur noch die Funktion [f4] ausgeführt werden;

Die Ergebnisse der Ausführung in PyCharm lauten wie folgt:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/07/main.py
f3
f4
done

Process finished with exit code 0

In einem Python-Terminal außerhalb des Verzeichnisses des Hauptskripts sehen die Ergebnisse wie folgt aus:

1
2
3
4
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 07/main.py
f3
f4
done

Von nun an werden wir immer nach dem gleichen Verfahren vorgehen, um eine Anwendung zu konfigurieren:

  • das Vorhandensein eines Skripts [config.py] im Verzeichnis des Hauptskripts. Dieses Skript enthält eine Funktion [configure], die zwei Zwecke erfüllt:
    • Erstellung des Python-Pfads für die Anwendung. Dazu listet [config.py] alle Ordner auf, die die von der Anwendung verwendeten Module enthalten, und erstellt den Python-Pfad anhand ihrer absoluten Pfade;
    • Erstellung des [config]-Wörterbuchs für die Konfiguration der Anwendung;

Wir wenden diesen Ansatz auf die zweite Version der Übungsaufgabe an. Wie Sie sich vielleicht erinnern, funktionierte Version 1 in der PyCharm-Umgebung, nicht jedoch in einem Python-Terminal. Das Problem lag am Python-Pfad.