Dateien aus verschiedenen Ordnern importieren

1406

Ich habe die folgende Ordnerstruktur.

application/app/folder/file.py

und ich möchte einige Funktionen aus file.py in eine andere Python-Datei importieren, die sich in befindet

application/app2/some_folder/some_file.py

ich habe es versucht

from application.app.folder.file import func_name

und einige andere verschiedene Versuche, aber bisher konnte ich nicht richtig importieren. Wie kann ich das machen?

Ivan
quelle
2
Siehe auch
wim
Das Lesen der offiziellen Dokumentation hat mir sehr geholfen! docs.python.org/3/reference/…
Kavin Raju S

Antworten:

1442

Standardmäßig können Sie nicht. Beim Importieren einer Datei durchsucht Python nur das aktuelle Verzeichnis, das Verzeichnis, aus dem das Einstiegspunktskript ausgeführt wird, und sys.pathdas Speicherorte wie das Paketinstallationsverzeichnis enthält (es ist tatsächlich etwas komplexer als dieses, aber dies deckt die meisten Fälle ab). .

Sie können dem Python-Pfad jedoch zur Laufzeit Folgendes hinzufügen:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file
Cameron
quelle
355
sys.path.append('/path/to/application/app/folder')ist sauberer imo
pseudosudo
387
@pseudosudo: Ja, aber das Einfügen zu Beginn hat den Vorteil, dass bei Namenskonflikten sichergestellt wird, dass der Pfad vor anderen (auch integrierten) Pfaden durchsucht wird.
Cameron
8
@kreativitea - sys.pathgibt a zurück list, nicht a deque, und es wäre dumm, das listin a dequeund zurück umzuwandeln .
ArtOfWarfare
37
Wird es als pythonische Methode zum Verwalten von .py-Dateien in Ordnern angesehen? Ich frage mich ... warum wird es nicht standardmäßig unterstützt? Es ist nicht sinnvoll, alle .py-Dateien in einem einzigen Verzeichnis zu verwalten.
Ofir
49
@Ofir: Nein, das ist keine schöne, saubere Python-Lösung. Im Allgemeinen sollten Sie Pakete verwenden (die auf Verzeichnisbäumen basieren). Diese Antwort war spezifisch für die gestellte Frage und hat aus irgendeinem Grund weiterhin eine große Anzahl von positiven Stimmen.
Cameron
709

Nichts falsch mit:

from application.app.folder.file import func_name

Stellen Sie einfach sicher, dass folderauch ein enthalten ist __init__.py, damit es als Paket enthalten sein kann. Ich bin mir nicht sicher, warum die anderen Antworten darüber sprechen PYTHONPATH.

Joey
quelle
47
Weil dies nicht die Fälle abdeckt, in denen Änderungen PYTHONPATHerforderlich sind. Angenommen, Sie haben zwei Ordner auf derselben Ebene: Aund B. Ahat eine __init.py__. Versuchen Sie, etwas von Binnen zu importieren A.
Msvalkon
32
Was ist in der init.pyoder __init__.pyDatei?
Lorem Ipsum Dolor
47
@ Xinyang Es kann eine leere Datei sein. Seine Existenz weist Python an, das Verzeichnis als Paket zu behandeln.
Jay
9
Natürlich geht diese Antwort davon aus, dass applicationund appbereits Pakete sind (dh Sie haben bereits __init__.pyin beiden). Als Ergebnis der Zugabe __init__.pyauch zu folder, application.app.folderwird zu einem (Teil-) Paket , von dem Sie den Zugriff auf Modul application.app.folder.file , dessen Symbol func_namejetzt importiert werden kann
Yibo Yang
30
Was auch immer ich versuche, das wird nicht funktionieren. Ich möchte aus einem "Geschwister" -Verzeichnis importieren, also eins nach oben nach unten. Alle haben __ init __. Py, einschließlich der Eltern. Ist das Python 3-spezifisch?
dasWesen
110

Wenn sich Module an parallelen Standorten befinden, wie in der Frage:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Diese Abkürzung macht ein Modul für das andere sichtbar:

import sys
sys.path.append('../')
slizb
quelle
24
Als Einschränkung: Dies funktioniert, solange das importierende Skript aus dem enthaltenen Verzeichnis ausgeführt wird. Andernfalls wird das übergeordnete Verzeichnis des anderen Verzeichnisses, aus dem das Skript ausgeführt wird, an den Pfad angehängt, und der Import schlägt fehl.
Carl Smith
14
Um dies zu vermeiden, können wir das übergeordnete Verzeichnis der Dateisys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Das hat bei mir nicht funktioniert - ich musste dort einen zusätzlichen Dirnamen hinzufügen, um wieder zum Elternteil aufzusteigen, damit das Laufen cli/foo.pyvon der Kommandozeile aus möglich warimport cli.bar
RCross
2
@ Rahul, Ihre Lösung funktioniert nicht für interaktive Shells
towi_parallelism
Wenn Sie es von Ihrem Stammordner (dh Anwendungsordner) aus ausführen, können Sie sys.path.append('.')das Modul wahrscheinlich mit importieren from app2.some_folder.some_file import your_function. Alternativ läuft das, was für mich funktioniert, python3 -m app2.another_folder.another_fileaus dem Stammordner.
süchtig
68

Erstes import sys in name-file.py

 import sys

Zweitens hängen Sie den Ordnerpfad in name-file.py an

sys.path.insert(0, '/the/folder/path/name-package/')

Drittens Erstellen Sie eine leere Datei mit dem Namen __ init __.py in Ihrem Unterverzeichnis (dies teilt Python mit, dass es sich um ein Paket handelt).

  • name-file.py
  • Namenspaket
    • __ init __.py.
    • name-module.py

Viertens importieren Sie das Modul in den Ordner in name-file.py

from name-package import name-module
Alex Montoya
quelle
5
Da sich der Name-Ordner direkt unter name-file.py befindet, sollte dies auch ohne den--ommand funktionieren sys.path.insert. Daher bleibt die Antwort die Frage, ob diese Lösung auch dann funktioniert, wenn sich der Namensordner an einem beliebigen Ort befindet.
Bastian
55

Ich denke, ein Ad-hoc-Weg wäre die Verwendung der Umgebungsvariablen,PYTHONPATH wie in der Dokumentation beschrieben: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
quelle
Warten Sie, würde ich myScripts durch den Dateinamen ersetzen?
Wladimir Putin
4
Nein, mit dem Pfad des Verzeichnisses zu Ihrer .py-Datei
Ax3l
1
Wenn Sie Anaconda verwenden, funktioniert dies leider nicht, da PYTHONPATH unter der Haube intern nicht wirklich verwendet wird!
information_interchange
Informationen zu (jüngsten) Änderungen in Anaconda finden Sie in dieser SO für Workflows und Kommentare zu Workarounds : stackoverflow.com/questions/17386880/… Erstellen und installieren Sie im Allgemeinen kleine Pakete, anstatt die Importverzeichnisse zu hacken.
Ax3l
46

Die Antworten hier sind unklar, dies wird auf Python 3.6 getestet

Mit dieser Ordnerstruktur:

main.py
|
---- myfolder/myfile.py

Wo myfile.pyhat der Inhalt:

def myfunc():
    print('hello')

Die Importanweisung in main.pylautet:

from myfolder.myfile import myfunc
myfunc()

und das wird hallo drucken .

danday74
quelle
9
Das Hinzufügen einer init .py (leeren) Konfigurationsdatei in meinem Ordner funktionierte für mich unter Linux (y)
Vincent
7
@ Vincent meintest du __init__.py?
Mrgloom
2
@ Mrgloom in der Tat
Vincent
3
Aus irgendeinem Grund __init__.pyfunktioniert das Hinzufügen bei mir nicht. Ich benutze Py 3.6.5 unter Ubuntu 18. Es funktioniert auf Pycharm, aber nicht vom Terminal
Crearo Rotar
9
Dies hat nichts mit der Frage zu tun, ob Dateien aus einem anderen Zweig des Dateibaums als dem aktuellen Arbeitsverzeichnis importiert werden sollen.
Alexander Rossa
44

Ihr Problem ist, dass Python im Python-Verzeichnis nach dieser Datei sucht und sie nicht findet. Sie müssen angeben, dass Sie über das Verzeichnis sprechen, in dem Sie sich befinden, und nicht über das Python-Verzeichnis.

Um dies zu tun, ändern Sie dies:

from application.app.folder.file import func_name

dazu:

from .application.app.folder.file import func_name

Wenn Sie den Punkt hinzufügen, den Sie sagen, suchen Sie in diesem Ordner nach dem Anwendungsordner, anstatt im Python-Verzeichnis zu suchen.

CianB
quelle
2
Das hat mich
Phi
32

Soweit ich weiß, fügen Sie eine __init__.pyDatei direkt in den Ordner der Funktionen ein, die Sie importieren möchten.

Vaibhav Singh
quelle
7
Nur wenn sich das Skript, das dieses andere Verzeichnis einschließen möchte, bereits im sys.path befindet
Ax3l
2
Ich habe sys.path.append(tools_dir)unter Windows verwendet und muss kein __init__.py' file in my directory tools_dir` hinzufügen
herve-guerin
25

In Python 3.4 und höher können Sie direkt aus einer Quelldatei importieren (Link zur Dokumentation) . Dies ist nicht die einfachste Lösung, aber der Vollständigkeit halber füge ich diese Antwort hinzu.

Hier ist ein Beispiel. Zunächst die zu importierende Datei mit dem Namen foo.py:

def announce():
    print("Imported!")

Der Code, der die obige Datei importiert, ist stark vom Beispiel in der Dokumentation inspiriert:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

Die Ausgabe:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Beachten Sie, dass der Variablenname, der Modulname und der Dateiname nicht übereinstimmen müssen. Dieser Code funktioniert immer noch:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

Die Ausgabe:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Das programmgesteuerte Importieren von Modulen wurde in Python 3.1 eingeführt und bietet Ihnen mehr Kontrolle darüber, wie Module importiert werden. Weitere Informationen finden Sie in der Dokumentation.

wecsam
quelle
11
Ich weiß nicht, ob jemand versucht hat, das zu verstehen, aber ich denke, dass es zu kompliziert ist.
Dan
23

Arbeitete für mich in Python3 unter Linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
quelle
6
sys.path.append("/home/linux/folder/")- "~/folder/"
Stellen
22

Probieren Sie die relativen Importe von Python aus:

from ...app.folder.file import func_name

Jeder führende Punkt ist eine weitere höhere Ebene in der Hierarchie, beginnend mit dem aktuellen Verzeichnis.


Probleme? Wenn dies bei Ihnen nicht funktioniert, werden Sie wahrscheinlich von den vielen relativen Importen von gotcha gebissen. Lesen Sie die Antworten und Kommentare, um weitere Informationen zu erhalten: So beheben Sie "Versuchter relativer Import in Nicht-Paketen" auch mit __init__.py

Hinweis: haben __init__.pyauf jeder Verzeichnisebene. Möglicherweise benötigen Sie python -m application.app2.some_folder.some_file(ohne .py), das Sie aus dem Verzeichnis der obersten Ebene ausführen, oder haben dieses Verzeichnis der obersten Ebene in Ihrem PYTHONPATH. Puh!

Zectbumo
quelle
22

Ich stand vor der gleichen Herausforderung, insbesondere beim Importieren mehrerer Dateien. Auf diese Weise konnte ich sie überwinden.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name
Erick Mwazonga
quelle
2
Ihre Antwort wäre hilfreicher, wenn Sie erklären könnten, was es anders macht als ein gewöhnlicher Import?
not2qubit
1
Ich hatte /path/dir1/__init__.py und /path/dir1/mod.py. Für /path/some.py aus dir1.mod funktionierte der Import. In /path/dir2/some.py funktionierte es erst, nachdem ich die obige Antwort oben in die Datei kopiert und eingefügt hatte. Ich wollte meinen Pfad nicht bearbeiten, da sich nicht jedes Python-Projekt in / path / befindet.
Jwal
19

Betrachtet man applicationals Stammverzeichnis für Ihr Python - Projekt, erstellen Sie eine leere __init__.pyDatei in application, appund folderOrdner. some_file.pyNehmen Sie dann wie folgt Änderungen vor, um die Definition von func_name zu erhalten:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
ChandanK
quelle
sollte sein: sys.path.insert (0, r '/ from / root / directory')
Bolaka
18

Die Verwendung von sys.path.append mit einem absoluten Pfad ist nicht ideal, wenn Sie die Anwendung in andere Umgebungen verschieben. Die Verwendung eines relativen Pfads funktioniert nicht immer, da das aktuelle Arbeitsverzeichnis davon abhängt, wie das Skript aufgerufen wurde.

Da die Struktur des Anwendungsordners festgelegt ist, können wir os.path verwenden, um den vollständigen Pfad des zu importierenden Moduls abzurufen. Wenn dies beispielsweise die Struktur ist:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

Angenommen, Sie möchten das "Mango" -Modul importieren. In vanilla.py können Sie Folgendes tun:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Natürlich benötigen Sie die Variable mango_dir nicht.

Um zu verstehen, wie dies funktioniert, sehen Sie sich dieses interaktive Sitzungsbeispiel an:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

Überprüfen Sie auch die Dokumentation zu os.path .

Nagev
quelle
13

Das funktioniert bei mir unter Windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file
Emeeus
quelle
10

Ich bin etwas ganz Besonderes: Ich benutze Python mit Windows!

Ich vervollständige nur die Informationen: Sowohl für Windows als auch für Linux funktionieren sowohl relative als auch absolute Pfade sys.path(ich benötige relative Pfade, da ich meine Skripte auf den verschiedenen PCs und unter verschiedenen Hauptverzeichnissen verwende).

Und bei Verwendung von Windows beide \und /können als Trennzeichen für Dateinamen verwendet werden und natürlich müssen Sie doppelt \in Python - Strings,
einige gültige Beispiele:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(Hinweis: Ich denke, das /ist bequemer als ein \Ereignis, wenn es weniger "Windows-nativ" ist, weil es Linux-kompatibel und einfacher zu schreiben und in den Windows Explorer zu kopieren ist.)

Herve-Guerin
quelle
4
os.path.join ('tools', 'mydir')
Corey Goldberg
7

Wenn der Zweck des Ladens eines Moduls aus einem bestimmten Pfad darin besteht, Sie bei der Entwicklung eines benutzerdefinierten Moduls zu unterstützen, können Sie im selben Ordner des Testskripts einen symbolischen Link erstellen, der auf das Stammverzeichnis des benutzerdefinierten Moduls verweist. Diese Modulreferenz hat Vorrang vor allen anderen Modulen mit demselben Namen für alle in diesem Ordner ausgeführten Skripte.

Ich habe dies unter Linux getestet, aber es sollte in jedem modernen Betriebssystem funktionieren, das symbolische Links unterstützt.

Ein Vorteil dieses Ansatzes besteht darin, dass Sie auf ein Modul verweisen können, das sich in Ihrer eigenen lokalen SVC-Zweigarbeitskopie befindet. Dies kann die Entwicklungszykluszeit erheblich vereinfachen und die Fehlermodi bei der Verwaltung verschiedener Versionen des Moduls verringern.

Timothy C. Quinn
quelle
7

In meinem Fall musste ich eine Klasse importieren. Meine Datei sah folgendermaßen aus:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

In meine Hauptdatei habe ich den Code aufgenommen über:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
schmudu
quelle
1
@ not2qubit sys wurde in der Antwort nicht importiert.
Walter
7

Ich bin mehrmals auf dieselbe Frage gestoßen, daher möchte ich meine Lösung mitteilen.

Python-Version: 3.X.

Die folgende Lösung richtet sich an Personen, die Ihre Anwendung in Python Version 3.X entwickeln, da Python 2 seit dem 1. Januar 2020 nicht mehr unterstützt wird .

Projektstruktur

In Python 3 benötigen __init__.pySie aufgrund der impliziten Namespace-Pakete kein Projekt-Unterverzeichnis . Siehe Ist init .py für Pakete in Python 3.3+ nicht erforderlich?

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Problemstellung

In file_b.py, würde Ich mag eine Klasse importieren , Ain file_a.pyunter dem Ordner ein.

Lösungen

# 1 Ein schneller, aber schmutziger Weg

Ohne das Paket zu installieren, wie Sie gerade ein neues Projekt entwickeln

Verwenden Sie die try catch, um zu überprüfen, ob die Fehler vorliegen. Codebeispiel:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Installieren Sie Ihr Paket

Sobald Sie Ihre Anwendung installiert haben (in diesem Beitrag ist das Tutorial zur Installation nicht enthalten)

Sie können einfach

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Viel Spaß beim Codieren!

WY Hsu
quelle
Für weitere Informationen über absolute Importe
WY Hsu
3

Ich habe an einem Projekt gearbeitet a, über das Benutzer pip install amit der folgenden Dateiliste installieren sollen :

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py.

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py.

dogs = 1

Ich habe das Modul installiert, indem ich Folgendes aus dem Verzeichnis mit ausgeführt habe MANIFEST.in:

python setup.py install

Dann /moustache/armwrestlekonnte ich von einem völlig anderen Ort in meinem Dateisystem aus Folgendes ausführen:

import a
dir(a)

Was bestätigte, dass a.catstatsächlich gleich 0 und a.b.dogstatsächlich gleich 1 war, wie beabsichtigt.

duhaime
quelle
2

Anstatt nur eine zu import ...tun, tun Sie Folgendes:

from <MySubFolder> import <MyFile>

MyFile befindet sich im MySubFolder.

Galileoomega
quelle
1

Sie können die Python-Shell aktualisieren, indem Sie f5 drücken oder zu Run-> Run Module gehen. Auf diese Weise müssen Sie das Verzeichnis nicht ändern, um etwas aus der Datei zu lesen. Python ändert automatisch das Verzeichnis. Wenn Sie jedoch mit verschiedenen Dateien aus verschiedenen Verzeichnissen in der Python-Shell arbeiten möchten, können Sie das Verzeichnis in sys ändern, wie Cameron bereits sagte .

IOstream
quelle
1

Also hatte ich einfach mit der rechten Maustaste auf meine IDE geklickt und eine neue hinzugefügt folderund mich gefragt, warum ich nicht in der Lage war, daraus zu importieren. Später wurde mir klar, dass ich mit der rechten Maustaste klicken und ein Python-Paket erstellen muss, und keinen klassischen Dateisystemordner. Oder eine Post-Mortem-Methode, bei der ein hinzugefügt wird __init__.py(wodurch Python den Dateisystemordner als Paket behandelt), wie in anderen Antworten erwähnt. Fügen Sie diese Antwort hier hinzu, nur für den Fall, dass jemand diesen Weg gegangen ist.

Mithunpaul
quelle
1

Mit importlib können Sie Module importieren, in die Sie ein Modul aus einem Ordner importieren möchten, indem Sie eine Zeichenfolge wie folgt verwenden:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

Dieses Beispiel hat eine main.py, bei der es sich um den obigen Code handelt, dann einen Ordner namens Scripts. Anschließend können Sie in diesem Ordner alles aufrufen, was Sie benötigen, indem Sie die scriptNameVariable ändern . Sie können dann verwenden, scriptum auf dieses Modul zu verweisen. Wenn ich beispielsweise eine Funktion Hello()im Snake-Modul habe, können Sie diese Funktion folgendermaßen ausführen:

script.Hello()

Ich habe dies in Python 3.6 getestet

Dextron
quelle
0

Ich hatte diese Probleme einige Male. Ich bin viel auf dieselbe Seite gekommen. In meinem letzten Problem musste ich das serveraus einem festen Verzeichnis ausführen , aber beim Debuggen wollte ich aus verschiedenen Unterverzeichnissen ausführen.

import sys
sys.insert(1, /path) 

hat bei mir NICHT funktioniert, da ich in verschiedenen Modulen verschiedene * .csv- Dateien lesen musste, die sich alle im selben Verzeichnis befanden.

Am Ende war das, was für mich funktionierte, nicht pythonisch, aber:

Ich habe if __main__ oben auf dem Modul, das ich debuggen wollte, ein Modul verwendet , das von einem anderen als dem üblichen Pfad ausgeführt wird.

Damit:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
B Furtado
quelle