Ich möchte von einer Klasse in einer Datei erben, die sich in einem Verzeichnis über dem aktuellen befindet.
Ist es möglich, diese Datei relativ zu importieren?
Ich möchte von einer Klasse in einer Datei erben, die sich in einem Verzeichnis über dem aktuellen befindet.
Ist es möglich, diese Datei relativ zu importieren?
Antworten:
from ..subpkg2 import mod
Gemäß den Python-Dokumenten: Verwenden Sie in einer Pakethierarchie zwei Punkte, wie im Dokument mit den Importanweisungen angegeben :
Wenn Sie angeben, welches Modul importiert werden soll, müssen Sie nicht den absoluten Namen des Moduls angeben. Wenn ein Modul oder Paket in einem anderen Paket enthalten ist, ist es möglich, einen relativen Import innerhalb desselben Top-Pakets durchzuführen, ohne den Paketnamen angeben zu müssen. Durch die Verwendung führender Punkte im angegebenen Modul oder Paket
from
können Sie festlegen, wie hoch die aktuelle Pakethierarchie durchlaufen werden soll, ohne genaue Namen anzugeben. Ein führender Punkt bedeutet das aktuelle Paket, in dem das Modul existiert, das den Import vornimmt. Zwei Punkte bedeuten eine Paketebene . Drei Punkte sind zwei Ebenen höher. Wenn Sie alsofrom . import mod
von einem Modul impkg
Paket aus ausführen, werden Sie am Ende importierenpkg.mod
. Wenn Siefrom ..subpkg2 import mod
von innen ausführen, werdenpkg.subpkg1
Sie importierenpkg.subpkg2.mod
. Die Spezifikation für relative Importe ist in PEP 328 enthalten .
PEP 328 befasst sich mit absoluten / relativen Importen.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Die Antwort von @ gimel ist richtig, wenn Sie die von ihm erwähnte Pakethierarchie garantieren können. Wenn Sie nicht können - wenn Ihr wirklicher Bedarf so ist, wie Sie ihn ausgedrückt haben, ausschließlich an Verzeichnisse gebunden ist und keine notwendige Beziehung zur Verpackung besteht -, müssen Sie daran arbeiten __file__
, das übergeordnete Verzeichnis herauszufinden (ein paar os.path.dirname
Aufrufe reichen aus; -), dann (wenn das Verzeichnis nicht bereits eingeschaltet ist sys.path
) prepend vorübergehend den Einsatz dir gleich am Anfang von sys.path
, __import__
, sagte Entfernen dir wieder - chaotisch Arbeit in der Tat, aber, „wenn es sein muss, müssen Sie“ (und Pyhon ist bestrebt, nie den Programmierer aufhören zu tun , was muss getan werden - ebenso wie die Norm ISO C sagt in der „Spirit of C“ in seinem Vorwort -!).
Hier ist ein Beispiel, das für Sie funktionieren könnte:
import sys
import os.path
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import module_in_parent_dir
sys.path
um dasselbe Modul unter verschiedenen Namen und allen entsprechenden Fehlern verfügbar zu machen. autopath.py in pypy oder _preamble.py in twisted lösen es mithilfe eines Suchkriteriums, das das Paket der obersten Ebene identifiziert, während die Verzeichnisse nach oben durchlaufen werden.
sys.path.remove(pathYouJustAdded)
nach dem Import etwas tun, das Sie benötigen, um diesen neuen Pfad nicht beizubehalten.
Importieren Sie das Modul aus einem Verzeichnis, das genau eine Ebene über dem aktuellen Verzeichnis liegt:
from .. import module
from .. import module
Ich habe einen Fehler erhalten ValueError: Versucht, relativen Import in Nicht-Paket durch
Vorwort: Ich habe eine frühere Antwort grundlegend umgeschrieben, in der Hoffnung, den Menschen den Einstieg in das Python-Ökosystem zu erleichtern und hoffentlich allen die beste Änderung des Erfolgs mit dem Python-Importsystem zu ermöglichen.
Dies wird relative Importe innerhalb eines Pakets abdecken , was meiner Meinung nach der wahrscheinlichste Fall für die Frage von OP ist.
Aus diesem Grund schreiben wir, import foo
um ein Modul "foo" aus dem Root-Namespace zu laden, anstatt zu schreiben:
foo = dict(); # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh: # please avoid doing this
exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo) # please avoid doing this
Aus diesem Grund können wir Python in Umgebungen einbetten, in denen es kein defacto-Dateisystem gibt, ohne ein virtuelles wie Jython bereitzustellen.
Durch die Entkopplung von einem Dateisystem können Importe flexibel durchgeführt werden. Dieses Design ermöglicht beispielsweise Importe aus Archiv- / Zip-Dateien, Import-Singletons, Bytecode-Caching, CFFI-Erweiterungen und sogar das Laden von Remote-Codedefinitionen.
Wenn Importe also nicht an ein Dateisystem gekoppelt sind, was bedeutet "ein Verzeichnis hoch"? Wir müssen einige Heuristiken auswählen, aber wir können dies tun, zum Beispiel wenn wir innerhalb eines Pakets arbeiten . Einige wurden bereits definiert, die relative Importe wie .foo
und..foo
innerhalb desselben Pakets funktionieren lassen. Cool!
Wenn Sie Ihre Quellcode-Lademuster aufrichtig mit einem Dateisystem koppeln möchten, können Sie dies tun. Sie müssen Ihre eigenen Heuristiken auswählen und eine Art Importmaschine verwenden. Ich empfehle importlib
Das Importlib-Beispiel von Python sieht ungefähr so aus:
import importlib.util
import sys
# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'
foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)
foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
Ein großartiges Beispielprojekt ist offiziell hier verfügbar: https://github.com/pypa/sampleproject
Ein Python-Paket ist eine Sammlung von Informationen zu Ihrem Quellcode, die andere Tools darüber informieren können, wie Sie Ihren Quellcode auf andere Computer kopieren und Ihren Quellcode in den Pfad dieses Systems integrieren, damit er import foo
für andere Computer funktioniert (unabhängig vom Interpreter). Host-Betriebssystem usw.)
Lassen Sie uns foo
in einem Verzeichnis einen Paketnamen haben (vorzugsweise in einem leeren Verzeichnis).
some_directory/
foo.py # `if __name__ == "__main__":` lives here
Ich bevorzuge es, setup.py
als Geschwister zu erstellen foo.py
, da dies das Schreiben der Datei setup.py vereinfacht. Sie können jedoch eine Konfiguration schreiben, um alles zu ändern / umzuleiten, was setuptools standardmäßig tut, wenn Sie möchten. Zum Beispiel ist das Einfügen foo.py
in ein "src /" - Verzeichnis etwas populär, das hier nicht behandelt wird.
some_directory/
foo.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
py_modules=['foo'],
)
.
python3 -m pip install --editable ./ # or path/to/some_directory/
"editierbar" aka -e
wird die Importmaschinerie erneut umleiten, um die Quelldateien in dieses Verzeichnis zu laden, und stattdessen die aktuellen genauen Dateien in die Bibliothek der Installationsumgebung kopieren. Dies kann auch zu Verhaltensunterschieden auf dem Computer eines Entwicklers führen. Testen Sie unbedingt Ihren Code! Es gibt andere Tools als pip, aber ich würde pip als Einführung empfehlen :)
Ich möchte auch foo
ein "Paket" (ein Verzeichnis mit __init__.py
) anstelle eines Moduls (eine einzelne ".py" -Datei) erstellen. Sowohl "Pakete" als auch "Module" können in den Root-Namespace geladen werden. Module ermöglichen verschachtelte Namespaces. Dies ist hilfreich, wenn ein Import von "relativ einem Verzeichnis" gewünscht wird.
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
Ich mache auch gerne ein foo/__main__.py
, dies ermöglicht Python, das Paket als Modul python3 -m foo
auszuführen , zB wird ausgeführt foo/__main__.py
als __main__
.
some_directory/
foo/
__init__.py
__main__.py # `if __name__ == "__main__":` lives here, `def main():` too!
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
...
entry_points={
'console_scripts': [
# "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
'foo=foo.__main__:main',
]
},
)
Lassen Sie uns dies mit einigen weiteren Modulen konkretisieren: Grundsätzlich können Sie eine Verzeichnisstruktur wie folgt haben:
some_directory/
bar.py # `import bar`
foo/
__init__.py # `import foo`
__main__.py
baz.py # `import foo.baz
spam/
__init__.py # `import foo.spam`
eggs.py # `import foo.spam.eggs`
setup.py
setup.py
Enthält herkömmlicherweise Metadateninformationen zum Quellcode, z. B.:
foo
, obwohl es beliebt ist, Bindestriche durch Unterstriche zu ersetzenpython ./setup.py test
Es ist sehr umfangreich und kann sogar c-Erweiterungen im laufenden Betrieb kompilieren, wenn ein Quellmodul auf einem Entwicklungscomputer installiert wird. Für ein tägliches Beispiel empfehle ich die setup.py des PYPA Sample Repository
Wenn Sie ein Build-Artefakt freigeben, z. B. eine Kopie des Codes, mit dem nahezu identische Computer ausgeführt werden sollen, ist eine Datei "require.txt" eine beliebte Methode, um genaue Abhängigkeitsinformationen zu erfassen, wobei "install_requires" eine gute Methode zum Erfassen von Minimum und ist maximal kompatible Versionen. Da die Zielcomputer ohnehin nahezu identisch sind, empfehle ich dringend, einen Tarball mit einem gesamten Python-Präfix zu erstellen. Dies kann schwierig und zu detailliert sein, um hier darauf einzugehen. Schauen Sie sich pip install
die --target
Option an oder virtualenv aka venv für Leads.
zurück zum Beispiel
Wenn wir von foo / spam / eggs.py Code von foo / baz wollten, könnten wir ihn anhand seines absoluten Namespace anfordern:
import foo.baz
Wenn wir die Fähigkeit reservieren wollten, eier.py in Zukunft mit einer anderen relativen baz
Implementierung in ein anderes Verzeichnis zu verschieben , könnten wir einen relativen Import verwenden wie:
import ..baz
Um Python-Code zuverlässig zu laden, müssen Sie diesen Code in einem Modul und dieses Modul in der Python-Bibliothek installieren.
Installierte Module können immer aus dem Namespace der obersten Ebene mit geladen werden import <name>
Es gibt ein großartiges Beispielprojekt, das offiziell hier verfügbar ist: https://github.com/pypa/sampleproject
Grundsätzlich können Sie eine Verzeichnisstruktur wie folgt haben:
the_foo_project/
setup.py
bar.py # `import bar`
foo/
__init__.py # `import foo`
baz.py # `import foo.baz`
faz/ # `import foo.faz`
__init__.py
daz.py # `import foo.faz.daz` ... etc.
.
Seien Sie sicher , dass Ihr zu erklären , setuptools.setup()
in setup.py
,
offizielles Beispiel: https://github.com/pypa/sampleproject/blob/master/setup.py
In unserem Fall möchten wir wahrscheinlich exportieren bar.py
und foo/__init__.py
mein kurzes Beispiel:
#!/usr/bin/env python3
import setuptools
setuptools.setup(
...
py_modules=['bar'],
packages=['foo'],
...
entry_points={},
# Note, any changes to your setup.py, like adding to `packages`, or
# changing `entry_points` will require the module to be reinstalled;
# `python3 -m pip install --upgrade --editable ./the_foo_project
)
.
Jetzt können wir unser Modul in der Python-Bibliothek installieren. Mit pip können Sie the_foo_project
im Bearbeitungsmodus in Ihre Python-Bibliothek installieren , sodass wir in Echtzeit daran arbeiten können
python3 -m pip install --editable=./the_foo_project
# if you get a permission error, you can always use
# `pip ... --user` to install in your user python library
.
Jetzt können wir aus jedem Python-Kontext unsere gemeinsam genutzten py_modules und Pakete laden
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
, fast immer in einer virtuellen Umgebung. Ich schreibe fast nie ein Modul, das nicht installiert werden soll. Wenn ich etwas falsch verstehe, würde ich es gerne wissen.
editable
Ei-Links und installierte Python-Module nicht in jeder Hinsicht exakt gleich sind. Wenn Sie beispielsweise einem bearbeitbaren Modul einen neuen Namespace hinzufügen, finden Sie ihn in Ihrer Pfadsuche. Wenn dieser jedoch nicht in Ihre Datei setup.py exportiert wird, wird er nicht gepackt / installiert! Testen Sie Ihren Anwendungsfall :)