Wie mache ich relative Importe in Python?

527

Stellen Sie sich diese Verzeichnisstruktur vor:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

Ich codiere mod1und muss etwas importieren mod2. Wie soll ich das machen

Ich habe es versucht, from ..sub2 import mod2aber ich erhalte einen "Versuchten relativen Import in Nicht-Paket".

Ich googelte herum, fand aber nur " sys.pathManipulations" -Hacks. Gibt es keinen sauberen Weg?


Bearbeiten: Alle meine __init__.pysind derzeit leer

Edit2: Ich versuche , dies zu tun , weil sub2 Klassen enthält , die über Unter Pakete geteilt werden ( sub1, subXusw.).

Edit3: Das gesuchte Verhalten ist das gleiche wie in PEP 366 beschrieben (danke John B)

Joril
quelle
8
Ich empfehle, Ihre Frage zu aktualisieren, um klarer zu machen, dass Sie das in PEP 366 behandelte Problem beschreiben.
John B
2
Es ist eine langwierige Erklärung, aber überprüfen Sie hier: stackoverflow.com/a/10713254/1267156 Ich habe eine sehr ähnliche Frage beantwortet. Ich hatte das gleiche Problem bis letzte Nacht.
Sevvy325
3
Für diejenigen, die ein Modul laden möchten, das sich an einem beliebigen Pfad befindet, siehe dies: stackoverflow.com/questions/67631/…
Evgeni Sergeev
2
In einem ähnlichen Zusammenhang ändert Python 3 die Standardbehandlung von Importen standardmäßig auf absolut. relative Importe müssen explizit angegeben werden.
Ross

Antworten:

337

Jeder scheint Ihnen sagen zu wollen, was Sie tun sollten, anstatt nur die Frage zu beantworten.

Das Problem ist, dass Sie das Modul als '__main__' ausführen, indem Sie mod1.py als Argument an den Interpreter übergeben.

Aus PEP 328 :

Relative Importe verwenden das Attribut __name__ eines Moduls, um die Position dieses Moduls in der Pakethierarchie zu bestimmen. Wenn der Name des Moduls keine Paketinformationen enthält (z. B. "__main__"), werden relative Importe so aufgelöst, als wäre das Modul ein Modul der obersten Ebene, unabhängig davon, wo sich das Modul tatsächlich im Dateisystem befindet.

In Python 2.6 wird die Möglichkeit hinzugefügt, Module relativ zum Hauptmodul zu referenzieren. PEP 366 beschreibt die Änderung.

Update : Laut Nick Coghlan besteht die empfohlene Alternative darin, das Modul innerhalb des Pakets mit dem Schalter -m auszuführen.

John B.
quelle
2
Die Antwort hier besteht darin, an jedem Einstiegspunkt in Ihr Programm mit sys.path herumzuspielen. Ich denke, das ist der einzige Weg, dies zu tun.
Nick Retallack
76
Die empfohlene Alternative besteht darin, Module in Paketen mit dem -mSwitch auszuführen , anstatt ihren Dateinamen direkt anzugeben.
Ncoghlan
127
Ich verstehe nicht: Wo ist die Antwort hier? Wie kann man Module in eine solche Verzeichnisstruktur importieren?
Tom
27
@ Tom: In diesem Fall würde mod1 from sub2 import mod2. Um mod1 in der App auszuführen, tun Sie dies python -m sub1.mod1.
Xiong Chiamiov
11
@XiongChiamiov: Bedeutet dies, dass Sie dies nicht tun können, wenn Ihr Python in eine Anwendung eingebettet ist, sodass Sie keinen Zugriff auf Python-Befehlszeilenoptionen haben?
LarsH
130

Hier ist die Lösung, die für mich funktioniert:

Ich mache die relativen Importe als from ..sub2 import mod2 und wenn ich dann ausführen möchte, mod1.pygehe ich in das übergeordnete Verzeichnis von appund führe das Modul mit dem Python-m-Schalter als aus python -m app.sub1.mod1.

Der wahre Grund, warum dieses Problem bei relativen Importen auftritt, besteht darin, dass relative Importe funktionieren, indem die __name__Eigenschaft des Moduls übernommen wird. Wenn das Modul direkt ausgeführt wird, __name__ist es auf gesetzt __main__und enthält keine Informationen zur Paketstruktur. Und deshalb beschwert sich Python über den relative import in non-packageFehler.

Mit dem Schalter -m stellen Sie Python die Paketstrukturinformationen zur Verfügung, mit denen die relativen Importe erfolgreich aufgelöst werden können.

Ich habe dieses Problem oft beim relativen Importieren festgestellt. Und nachdem ich alle vorherigen Antworten gelesen hatte, war ich immer noch nicht in der Lage, herauszufinden, wie ich es auf saubere Weise lösen kann, ohne dass ich in alle Dateien Code auf dem Boilerplate einfügen muss. (Obwohl einige der Kommentare dank @ncoghlan und @XiongChiamiov wirklich hilfreich waren)

Ich hoffe, dies hilft jemandem, der mit relativen Importproblemen zu kämpfen hat, denn das Durchlaufen von PEP macht wirklich keinen Spaß.

Pankaj
quelle
9
Beste Antwort IMHO: Erklärt nicht nur, warum OP das Problem hatte, sondern findet auch einen Weg, es zu lösen, ohne die Art und Weise zu ändern, wie seine Module importieren . Immerhin waren die relativen Importe von OP in Ordnung. Der Schuldige war der fehlende Zugriff auf äußere Pakete, wenn sie direkt als Skript ausgeführt wurden. Etwas, -mdas gelöst werden sollte.
MestreLion
26
Beachten Sie auch: Diese Antwort war 5 Jahre nach der Frage. Diese Funktionen waren zu diesem Zeitpunkt nicht verfügbar.
JeremyKun
1
Wenn Sie ein Modul aus demselben Verzeichnis importieren möchten, können Sie dies tun from . import some_module.
Rotareti
124
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Du rennst python main.py.
  2. main.py tut: import app.package_a.module_a
  3. module_a.py tut import app.package_b.module_b

Alternativ könnten 2 oder 3 verwenden: from app.package_a import module_a

Das wird funktionieren, solange Sie appin Ihrem PYTHONPATH haben. main.pykönnte dann überall sein.

Sie schreiben also ein setup.py, um das gesamte App-Paket und die Unterpakete in die Python-Ordner des Zielsystems und in die Skriptordner des Zielsystems zu kopieren (zu installieren) main.py.

nosklo
quelle
3
Hervorragende Antwort. Gibt es eine Möglichkeit, auf diese Weise zu importieren, ohne das Paket in PYTHONPATH zu installieren?
Auraham
4
Vorgeschlagene zusätzliche Lektüre: blog.habnab.it/blog/2013/07/21/python-packages-and-you
nosklo
6
Dann muss eines Tages der Name der App in test_app geändert werden. was würde passieren? Sie müssen alle Quellcodes ändern und app.package_b.module_b -> test_app.package_b.module_b importieren. Dies ist absolut schlechte Praxis ... Und wir sollten versuchen, den relativen Import innerhalb des Pakets zu verwenden.
Spybdai
49

"Guido betrachtet das Ausführen von Skripten innerhalb eines Pakets als Anti-Pattern" (abgelehntes PEP-3122 )

Ich habe so viel Zeit damit verbracht, nach einer Lösung zu suchen, verwandte Beiträge hier auf Stack Overflow zu lesen und mir zu sagen: "Es muss einen besseren Weg geben!". Sieht so aus, als gäbe es keine.

lesnik
quelle
10
Hinweis: Der bereits erwähnte pep-366 (ungefähr zur gleichen Zeit wie pep-3122 erstellt ) bietet dieselben Funktionen, verwendet jedoch eine andere abwärtskompatible Implementierung, dh wenn Sie ein Modul in einem Paket als Skript ausführen und explizite relative Importe verwenden möchten Darin können Sie es dann mit -mswitch: ausführen python -m app.sub1.mod1oder app.sub1.mod1.main()von einem Skript der obersten Ebene aus aufrufen (z. B. generiert aus den in setup.py definierten Eintrittspunkten von setuptools).
JFS
+1 für die Verwendung von Setuptools und Einstiegspunkten - dies ist eine geeignete Methode, um Skripte einzurichten, die von außen an einem genau definierten Ort ausgeführt werden, anstatt PYTHONPATH
RecencyEffect
38

Dies ist zu 100% gelöst:

  • App /
    • main.py.
  • die Einstellungen/
    • local_setings.py

Importieren Sie settings / local_setting.py in app / main.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
Роман Арсеньев
quelle
2
Vielen Dank! Alle Leute zwangen mich, mein Skript anders auszuführen, anstatt mir zu sagen, wie ich es innerhalb des Skripts lösen soll. Aber ich musste den Code ändern, um ihn zu verwenden sys.path.insert(0, "../settings")und dannfrom local_settings import *
Vit Bernatik
25
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

Ich verwende dieses Snippet, um Module aus Pfaden zu importieren. Ich hoffe, das hilft

iElectric
quelle
2
Ich verwende dieses Snippet in Kombination mit dem imp-Modul (wie hier erklärt [1]) mit großer Wirkung. [1]: stackoverflow.com/questions/1096216/…
Xiong Chiamiov
7
Wahrscheinlich sollte sys.path.append (Pfad) durch sys.path.insert (0, Pfad) und sys.path [-1] durch sys.path [0] ersetzt werden. Andernfalls importiert die Funktion das falsche Modul, wenn sich bereits ein Modul mit demselben Namen im Suchpfad befindet. Wenn sich beispielsweise "some.py" im aktuellen Verzeichnis befindet, importiert import_path ("/ imports / some.py") die falsche Datei.
Alex Che
Genau! Manchmal haben andere relative Importe Vorrang. Verwenden Sie sys.path.insert
iElectric
Wie würden Sie das Verhalten von x import y (oder *) replizieren?
Levesque
Es ist nicht klar, bitte geben Sie die vollständige Verwendung dieses Skripts an, um das OP-Problem zu lösen.
Mrgloom
21

Erklärung der nosklo'sAntwort mit Beispielen

Hinweis: Alle __init__.pyDateien sind leer.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

app / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app / package_b / fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py.

from app.package_b import fun_b
fun_b.print_b()

Wenn Sie es ausführen $ python main.py, wird Folgendes zurückgegeben:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py macht: from app.package_b import fun_b
  • fun_b.py tut es from app.package_a.fun_a import print_a

Also Datei in Ordner package_bverwendet Datei in Ordner package_a, was Sie wollen. Recht??

suhailvs
quelle
12

Dies ist leider ein sys.path-Hack, aber es funktioniert ganz gut.

Ich bin auf dieses Problem mit einer anderen Ebene gestoßen: Ich hatte bereits ein Modul mit dem angegebenen Namen, aber es war das falsche Modul.

Ich wollte Folgendes tun (das Modul, an dem ich arbeitete, war Modul 3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

Beachten Sie, dass ich mymodule bereits installiert habe, aber in meiner Installation nicht "mymodule1" habe.

und ich würde einen ImportError erhalten, weil versucht wurde, von meinen installierten Modulen zu importieren.

Ich habe versucht, einen sys.path.append zu erstellen, und das hat nicht funktioniert. Was funktionierte, war ein sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

So eine Art Hack, aber alles hat funktioniert! Denken Sie also daran, wenn Sie möchten, dass Ihre Entscheidung andere Pfade überschreibt , müssen Sie sys.path.insert (0, Pfadname) verwenden, damit es funktioniert! Dies war ein sehr frustrierender Knackpunkt für mich. Viele Leute sagen, dass sie die Funktion "Anhängen" an sys.path verwenden sollen, aber das funktioniert nicht, wenn Sie bereits ein Modul definiert haben (ich finde es sehr seltsam).

Garrett Berg
quelle
sys.path.append('../')funktioniert gut für mich (Python 3.5.2)
Nister
Ich denke, das ist in Ordnung, da es den Hack in der ausführbaren Datei lokalisiert und keine Auswirkungen auf andere Module hat, die von Ihren Paketen abhängen können.
Tom Russell
10

Lassen Sie mich dies hier nur als meine eigene Referenz setzen. Ich weiß, dass es kein guter Python-Code ist, aber ich brauchte ein Skript für ein Projekt, an dem ich arbeitete, und ich wollte das Skript in ein scriptsVerzeichnis stellen.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Milchpfleger
quelle
9

Wie @EvgeniSergeev in den Kommentaren zum OP sagt, können Sie Code aus einer .pyDatei an einem beliebigen Ort importieren mit:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Dies ist aus dieser SO-Antwort entnommen .

LondonRob
quelle
2

Aus Python doc ,

In Python 2.5 können Sie das Importverhalten mithilfe einer from __future__ import absolute_importDirektive 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 stringwird immer die Version der Standardbibliothek gefunden. Es wird empfohlen, dass Benutzer so viel wie möglich absolute Importe verwenden. Daher ist es vorzuziehen, mit dem Schreiben from pkg import stringIhres Codes zu beginnen

jung rhew
quelle
1

Ich fand es einfacher, die Umgebungsvariable "PYTHONPATH" auf den obersten Ordner zu setzen:

bash$ export PYTHONPATH=/PATH/TO/APP

dann:

import sub1.func1
#...more import

Natürlich ist PYTHONPATH "global", aber es hat mir noch keine Probleme bereitet.

Andrew_1510
quelle
Auf diese Weise virtualenvkönnen Sie im Wesentlichen Ihre Importanweisungen verwalten.
Byxor
1

Zusätzlich zu dem, was John B gesagt hat, scheint es, als ob das Setzen der __package__Variablen helfen sollte, anstatt zu ändern, __main__was andere Dinge vermasseln könnte. Aber soweit ich testen konnte, funktioniert es nicht ganz so, wie es sollte.

Ich habe das gleiche Problem und weder PEP 328 noch 366 lösen das Problem vollständig, da beide am Ende des Tages den Kopf des Pakets benötigen sys.path, soweit ich verstehen kann.

Ich sollte auch erwähnen, dass ich nicht gefunden habe, wie die Zeichenfolge formatiert werden soll, die in diese Variablen eingehen soll. Ist es "package_head.subfolder.module_name"oder was?

Gabriel
quelle
0

Sie müssen den Pfad des Moduls an Folgendes anhängen PYTHONPATH:

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"
Giorgos Myrianthous
quelle
1
Dies ist in etwa die gleichen wie Manipulieren sys.path, da sys.pathaus initialisiert wirdPYTHONPATH
Joril
@Joril Das ist richtig, sys.pathmuss aber im Quellcode fest codiert werden, im Gegensatz dazu PYTHONPATHist dies eine Umgebungsvariable und kann exportiert werden.
Giorgos Myrianthous