Wie importiere ich eine Python-Klasse, die sich in einem Verzeichnis oben befindet?

208

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?

user129975
quelle

Antworten:

175

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 fromkö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 also from . import modvon einem Modul im pkgPaket aus ausführen, werden Sie am Ende importieren pkg.mod. Wenn Sie from ..subpkg2 import modvon innen ausführen, werden pkg.subpkg1Sie importierenpkg.subpkg2.mod. Die Spezifikation für relative Importe ist in PEP 328 enthalten .

PEP 328 befasst sich mit absoluten / relativen Importen.

gimel
quelle
4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.
Pep 328 zeigt nur Python-Version: 2.4, 2,5, 2.6. Version 3 bleibt sachkundigeren Seelen überlassen.
Gimel
22
Dies löst den Fehler ValueError aus: Versuch eines relativen Imports über das Paket der obersten Ebene hinaus
Carlo
4
Ergebnisse in ImportError: Versuch eines relativen Imports ohne bekanntes übergeordnetes Paket
Rotkiv
115
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Sepero
quelle
1
das funktioniert bei mir. Nachdem ich dies hinzugefügt habe, kann ich übergeordnete Module direkt importieren, ohne ".." verwenden zu müssen.
Evan Hu
8
Das funktioniert grob gesagt nur, wenn der PWD-Wert der Anwendung - ihr aktueller Directeroy - ein Kind des Elternteils ist. Selbst wenn es funktioniert, können viele Arten von systemischen Änderungen es verdrängen.
Phlip
Dies funktionierte auch für mich beim Importieren von Modulen höherer Ebene. Ich benutze dies mit os.chdir (".."), um andere Dateien höherer Ebene zu laden.
Surendra Shrestha
Dies scheint den gleichen Fehler wie die obige Antwort von Gimel auszulösen.
Carlo
81

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.dirnameAufrufe 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
Alex Martelli
quelle
2
Dies kann ein Verzeichnis hinzufügen, das sich in einem Python-Paket befindet, sys.pathum 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.
JFS
4
Möglicherweise möchten Sie sys.path.remove(pathYouJustAdded)nach dem Import etwas tun, das Sie benötigen, um diesen neuen Pfad nicht beizubehalten.
Matthias
35

Importieren Sie das Modul aus einem Verzeichnis, das genau eine Ebene über dem aktuellen Verzeichnis liegt:

from .. import module
Sepero
quelle
38
Ich habe: versucht, relativen Import über das Top-Level-Paket hinaus :(
RicardoE
25
using from .. import module Ich habe einen Fehler erhalten ValueError: Versucht, relativen Import in Nicht-Paket durch
Befolgen
3

So laden Sie ein Modul, das ein Verzeichnis ist

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.

Python ist ein modulares System

Aus diesem Grund schreiben wir, import fooum 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

Python ist nicht an ein Dateisystem gekoppelt

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 .foound..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

Verpackung

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 foofür andere Computer funktioniert (unabhängig vom Interpreter). Host-Betriebssystem usw.)

Verzeichnisaufbau

Lassen Sie uns fooin einem Verzeichnis einen Paketnamen haben (vorzugsweise in einem leeren Verzeichnis).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Ich bevorzuge es, setup.pyals 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.pyin 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 -ewird 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 fooein "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 fooauszuführen , zB wird ausgeführt foo/__main__.pyals __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.:

  • Welche Abhängigkeiten werden für die Installation mit dem Namen "install_requires" benötigt?
  • Welcher Name für die Paketverwaltung verwendet werden soll ("Name" installieren / deinstallieren), ich schlage vor, dass dieser Name in unserem Fall mit Ihrem primären Python-Paketnamen übereinstimmt foo, obwohl es beliebt ist, Bindestriche durch Unterstriche zu ersetzen
  • Lizenzinformationen
  • Laufzeit-Tags (Alpha / Beta / etc),
  • Zielgruppentags (für Entwickler, für maschinelles Lernen usw.),
  • einseitiger Dokumentationsinhalt (wie eine README-Datei),
  • Shell-Namen (Namen, die Sie in der Benutzer-Shell wie bash eingeben, oder Namen, die Sie in einer grafischen Benutzer-Shell wie ein Startmenü finden),
  • eine Liste der Python-Module, die dieses Paket installiert (und deinstalliert)
  • ein defacto "Run Tests" Einstiegspunkt python ./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 installdie --targetOption an oder virtualenv aka venv für Leads.

zurück zum Beispiel

So importieren Sie eine Datei in einem Verzeichnis:

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 bazImplementierung in ein anderes Verzeichnis zu verschieben , könnten wir einen relativen Import verwenden wie:

import ..baz
ThorSummoner
quelle
-5

Python ist ein modulares System

Python ist nicht auf ein Dateisystem angewiesen

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.pyund foo/__init__.pymein kurzes Beispiel:

setup.py

#!/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_projectim 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

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
ThorSummoner
quelle
2
sollte erwähnen, dass ich mein Modul immer installiere, während ich daran arbeite 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.
ThorSummoner
Ich sollte auch erwähnen, dass die Verwendung einer Venv-Auto-Creation-Testsuite wie tox sehr hilfreich ist, da editableEi-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 :)
ThorSummoner