ich habe eine Frage zu absoluten Importen in Python beantwortet , die ich aufgrund des Lesens des Python 2.5-Änderungsprotokolls und des zugehörigen PEP zu verstehen glaubte . Bei der Installation von Python 2.5 und dem Versuch, ein Beispiel für die ordnungsgemäße Verwendung zu erstellen, stelle from __future__ import absolute_import
ich jedoch fest, dass die Dinge nicht so klar sind.
Direkt aus dem oben verlinkten Änderungsprotokoll fasste diese Aussage mein Verständnis der absoluten Importänderung genau zusammen:
Angenommen, Sie haben ein Paketverzeichnis wie das folgende:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Dies definiert ein Paket
pkg
mit dem Namenpkg.main
und denpkg.string
Submodulen.Betrachten Sie den Code im main.py-Modul. Was passiert, wenn die Anweisung ausgeführt wird?
import string
? In Python 2.4 und früheren Versionen wird zunächst im Verzeichnis des Pakets nach einem relativen Import gesucht, pkg / string.py gefunden, der Inhalt dieser Datei alspkg.string
Modul importiert und das Modul an den Namen"string"
impkg.main
Namespace des Moduls gebunden .
Also habe ich genau diese Verzeichnisstruktur erstellt:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
und string.py
sind leer. main.py
enthält den folgenden Code:
import string
print string.ascii_uppercase
Wie erwartet schlägt das Ausführen mit Python 2.5 mit einem fehl AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Weiter unten im 2.5-Änderungsprotokoll finden wir jedoch Folgendes (Hervorhebung hinzugefügt):
In Python 2.5 können Sie das
import
Verhalten mithilfe einerfrom __future__ import absolute_import
Direktive auf absolute Importe umstellen . Dieses absolute Importverhalten wird in einer zukünftigen Version (wahrscheinlich Python 2.7) zum Standard. Sobald absolute Importe die Standardeinstellung sind,import string
wird immer die Version der Standardbibliothek gefunden.
Ich habe also erstellt pkg/main2.py
, identisch mit, main.py
aber mit der zusätzlichen zukünftigen Importrichtlinie. Es sieht jetzt so aus:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Das Ausführen mit Python 2.5 schlägt jedoch fehl mit AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Dies steht im Widerspruch ziemlich flach die Aussage aus , import string
wird immer die std-lib - Version findet mit absoluten Importen aktiviert. Darüber hinaus habe ich trotz der Warnung, dass absolute Importe zum "neuen Standard" -Verhalten werden sollen, dasselbe Problem mit Python 2.7 mit oder ohne __future__
Anweisung festgestellt:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
sowie Python 3.5 mit oder ohne (vorausgesetzt, die print
Anweisung wird in beiden Dateien geändert):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
Ich habe andere Variationen davon getestet. Statt string.py
habe ich ein leeres Modul erstellt - ein Verzeichnis mit dem Namen string
nur ein leeren enthalten __init__.py
- und stattdessen die Einfuhr von Ausgabe aus main.py
, ich habe cd
‚dpkg
und die Importe direkt aus dem REPL laufen. Weder diese Variationen (noch eine Kombination davon) haben die obigen Ergebnisse verändert. Ich kann dies nicht mit dem vereinbaren, was ich über die __future__
Richtlinie und die absoluten Importe gelesen habe .
Es scheint mir, dass dies durch Folgendes leicht zu erklären ist (dies stammt aus den Python 2-Dokumenten, aber diese Aussage bleibt in denselben Dokumenten für Python 3 unverändert):
sys.path
(...)
Wie beim Programmstart initialisiert, ist das erste Element dieser Liste
path[0]
das Verzeichnis, das das Skript enthält, mit dem der Python-Interpreter aufgerufen wurde. Wenn das Skriptverzeichnis nicht verfügbar ist (z. B. wenn der Interpreter interaktiv aufgerufen wird oder wenn das Skript aus der Standardeingabe gelesen wird),path[0]
ist dies die leere Zeichenfolge, die Python anweist, zuerst die Module im aktuellen Verzeichnis zu suchen.
Also, was vermisse ich? Warum tut die __future__
Aussage scheinbar nicht das, was sie sagt, und wie lässt sich dieser Widerspruch zwischen diesen beiden Abschnitten der Dokumentation sowie zwischen beschriebenem und tatsächlichem Verhalten lösen?
quelle
Antworten:
Das Changelog ist schlampig formuliert.
from __future__ import absolute_import
kümmert sich nicht darum, ob etwas Teil der Standardbibliothek ist, und gibtimport string
Ihnen nicht immer das Standardbibliotheksmodul mit absoluten Importen.from __future__ import absolute_import
Wenn Sie diesimport string
tun, sucht Python immer nach einemstring
Modul der obersten Ebene und nicht nach einem Modul der obersten Ebenecurrent_package.string
. Dies hat jedoch keinen Einfluss auf die Logik, mit der Python entscheidet, welche Datei dasstring
Modul ist. Wenn Sie das tunpkg/script.py
sieht für Python nicht wie ein Teil eines Pakets aus. Nach den normalen Verfahren wird daspkg
Verzeichnis dem Pfad hinzugefügt, und alle.py
Dateien impkg
Verzeichnis sehen aus wie Module der obersten Ebene.import string
findetpkg/string.py
nicht, weil es einen relativen Import macht, sondern weilpkg/string.py
es das Modul der obersten Ebene zu sein scheintstring
. Die Tatsache, dass dies nicht das Standardbibliotheksmodul ist,string
kommt nicht zum Vorschein.Sie können die Datei als Teil des
pkg
Pakets ausführenIn diesem Fall ist die
pkg
Verzeichnis nicht zum Pfad hinzugefügt. Das aktuelle Verzeichnis wird jedoch zum Pfad hinzugefügt.Sie können auch ein Boilerplate hinzufügen
pkg/script.py
, damit Python es als Teil despkg
Pakets behandelt, selbst wenn es als Datei ausgeführt wird:Dies hat jedoch keine Auswirkungen
sys.path
. Sie benötigen eine zusätzliche Behandlung, um daspkg
Verzeichnis aus dem Pfad zu entfernen. Wenn sichpkg
das übergeordnete Verzeichnis nicht im Pfad befindet, müssen Sie dies auch im Pfad festhalten.quelle
import string
wenn Sie es versehentlich Schatten, zumindest ohne durch Drallsys.modules
. Ist das nicht das, wasfrom __future__ import absolute_import
verhindern soll? Was tut es? (PS, ich bin nicht der Downvoter.)sys.path
funktioniert, und die eigentliche Frage wurde überhaupt nicht beantwortet. Das heißt, was machtfrom __future__ import absolute_import
eigentlich?sys.modules
erhalten Sie das Standardbibliotheksmodul nicht,string
wenn Sie es mit Ihrem eigenen Modul der obersten Ebene beschattet haben.from __future__ import absolute_import
soll nicht verhindern, dass Module der obersten Ebene Module der obersten Ebene beschatten; Es soll verhindern, dass paketinterne Module Module der obersten Ebene beschatten. Wenn Sie die Datei als Teil despkg
Pakets ausführen , werden die internen Dateien des Pakets nicht mehr als oberste Ebene angezeigt.pkg
handelt sich um ein Paket im Importsuchpfadpython -m pkg.main
.-m
benötigt einen Modulnamen, keinen Dateipfad.Der Unterschied zwischen absoluten und relativen Importen kommt nur zum Tragen, wenn Sie ein Modul aus einem Paket importieren und dieses Modul ein anderes Submodul aus diesem Paket importiert. Sieh den Unterschied:
Bestimmtes:
Beachten Sie, dass
python2 pkg/main2.py
das Verhalten anders ist als das Startenpython2
und Importierenpkg.main2
(was der Verwendung von entspricht-m
Schalters entspricht).Wenn Sie jemals ein Submodul eines Pakets ausführen möchten, verwenden Sie immer den
-m
Schalter, der den Interpreter daran hindert, das zu verkettensys.path
Liste und die Semantik des Submoduls korrekt behandelt.Außerdem bevorzuge ich die Verwendung expliziter relativer Importe für Paket-Submodule, da diese im Fehlerfall mehr Semantik und bessere Fehlermeldungen bieten.
quelle
python2 pkg/main2.py
hat ein anderes Verhalten als das Starten von python2 und das Importieren von pkg.main2?pkg/main2.py
Python (Version 2) ist nicht zu behandelnpkg
als Paket. Berücksichtigenpython2 -m pkg.main2
Sie beim Verwenden oder Importieren, dass es sich umpkg
ein Paket handelt.