Importazione di file da cartelle diverse


1407

Ho la seguente struttura di cartelle.

application/app/folder/file.py

e voglio importare alcune funzioni da file.py in un altro file Python in cui risiede

application/app2/some_folder/some_file.py

ho provato

from application.app.folder.file import func_name

e alcuni altri vari tentativi, ma finora non sono riuscito a importare correttamente. Come posso fare questo?



Leggere la documentazione ufficiale mi ha aiutato molto! docs.python.org/3/reference/…
Kavin Raju S

Risposte:


1445

Per impostazione predefinita, non puoi. Quando si importa un file, Python cerca solo la directory corrente, la directory da cui è in esecuzione lo script del punto di ingresso e sys.pathche include posizioni come la directory di installazione del pacchetto (in realtà è un po 'più complessa di questa, ma copre la maggior parte dei casi) .

Tuttavia, è possibile aggiungere al percorso Python in fase di esecuzione:

# 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

356
sys.path.append('/path/to/application/app/folder')è più pulito imo
pseudosudo

387
@pseudosudo: Sì, lo è, ma inserendolo all'inizio ha il vantaggio di garantire che il percorso venga cercato prima degli altri (anche quelli incorporati) in caso di conflitti di denominazione.
Cameron,

8
@kreativitea - sys.pathrestituisce una list, non una deque, e sarebbe stupido per convertire la listad una dequee ritorno.
ArtOfWarfare il

37
È considerato un modo pitonico per gestire i file .py nelle cartelle? Mi chiedo ... perché non è supportato per impostazione predefinita? non ha senso mantenere tutti i file .py in una singola directory ..
Ofir

49
@Ofir: No, questa non è una bella soluzione pitonica pulita. In generale, dovresti usare i pacchetti (che sono basati su alberi di directory). Questa risposta era specifica alla domanda posta e per qualche motivo continua ad accumulare un gran numero di voti.
Cameron,

709

Niente di sbagliato in:

from application.app.folder.file import func_name

Assicurati solo che foldercontenga anche un __init__.py, questo consente di includerlo come pacchetto. Non sono sicuro del motivo per cui le altre risposte parlano PYTHONPATH.


47
Perché questo non copre i casi in cui PYTHONPATHè necessaria la modifica . Supponi di avere due cartelle allo stesso livello: Ae B. Aha un __init.py__. Prova a importare qualcosa Bdall'interno A.
msvalkon,

32
Cosa c'è dentro il file init.pyo __init__.py?
Lorem Ipsum Dolor,

48
@Xinyang Può essere un file vuoto. La sua stessa esistenza dice a Python di trattare la directory come un pacchetto.
jay

9
Ovviamente questa risposta presuppone che applicatione appsiano già pacchetti (cioè già presenti __init__.pyin entrambi). Come risultato dell'aggiunta __init__.pyanche a folder, application.app.folderdiventa un (sotto) pacchetto , dal quale è possibile accedere al modulo application.app.folder.file , il cui simbolo func_namepuò ora essere importato
Yibo Yang

30
Qualunque cosa io provi, questo non funzionerà. Voglio importare da una directory "fratello", quindi uno in alto uno in basso. Tutti hanno __ init __. Py, incluso il genitore. Questo Python è specifico per 3?
dasWesen,

111

Quando i moduli si trovano in posizioni parallele, come nella domanda:

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

Questa scorciatoia rende un modulo visibile all'altro:

import sys
sys.path.append('../')

24
Come avvertimento: funziona fintanto che lo script di importazione viene eseguito dalla sua directory di contenimento. Altrimenti la directory principale di qualunque altra directory da cui viene eseguito lo script verrà aggiunta al percorso e l'importazione non riuscirà.
Carl Smith,

14
Per evitarlo, possiamo ottenere la directory principale del filesys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul,

Questo non ha funzionato per me - ho dovuto aggiungere un altro dirname lì dentro per risalire al genitore, in modo che correre cli/foo.pydalla riga di comando fosse in grado diimport cli.bar
RCross,

2
@Rahul, la tua soluzione non funziona per le shell interattive
towi_parallelism

Se lo esegui dalla tua cartella principale (es. Cartella dell'applicazione), probabilmente stai bene sys.path.append('.')importando il modulo usando from app2.some_folder.some_file import your_function. In alternativa, ciò che funziona per me è in esecuzione python3 -m app2.another_folder.another_filedalla cartella principale.
dipendente il

68

Primo import sys in name-file.py

 import sys

Secondo aggiungere il percorso della cartella in name-file.py

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

Terzo Crea un file vuoto chiamato __ init __.py nella tua sottodirectory (questo dice a Python che è un pacchetto)

  • name-file.py
  • Nome pacchetto
    • __ init __.py
    • name-module.py

Quarto importare il modulo all'interno della cartella in name-file.py

from name-package import name-module

5
Con name-folder proprio sotto name-file.py, questo dovrebbe funzionare anche senza il sys.path.insertcomando-. Pertanto, la risposta lascia la domanda, se questa soluzione funziona anche quando la cartella dei nomi si trova in una posizione arbitraria.
Bastian,

55

Penso che un modo ad hoc sarebbe usare la variabile d'ambientePYTHONPATH come descritto nella documentazione: Python2 , Python3

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

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%

Aspetta, dovrei sostituire myScripts con il nome file?
Vladimir Putin,

4
no, con il percorso della directory al tuo file .py
Ax3l

1
Sfortunatamente, se stai usando Anaconda, questo non funzionerà, dal momento che PYTHONPATH non è davvero usato internamente!
information_interchange

Per (recenti) modifiche in anaconda, vedere questo SO per flussi di lavoro e commenti per soluzioni alternative: stackoverflow.com/questions/17386880/… In generale, creare e installare piccoli pacchetti invece di hackerare le directory di importazione.
Ax3l

47

Le risposte qui mancano di chiarezza, questo è testato su Python 3.6

Con questa struttura di cartelle:

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

Dove myfile.pyha il contenuto:

def myfunc():
    print('hello')

La dichiarazione di importazione in main.pyè:

from myfolder.myfile import myfunc
myfunc()

e questo stamperà ciao .


9
l'aggiunta di un file di configurazione init .py (vuoto) nella mia cartella ha funzionato per me su linux (y)
Vincent

7
@Vincent intendevi __init__.py?
MrGloom,

2
@mrgloom infatti
Vincent

3
Per qualche motivo l'aggiunta __init__.pynon funziona per me. Sto usando Py 3.6.5 su Ubuntu 18. Funziona su Pycharm ma non dal terminale
Crearo Rotar

9
Ciò è completamente estraneo alla domanda che si pone sull'importazione di file da un ramo diverso dell'albero dei file rispetto alla directory di lavoro corrente.
Alexander Rossa,

45

Il tuo problema è che Python sta cercando nella directory Python questo file e non lo trova. Devi specificare che stai parlando della directory in cui ti trovi e non di quella di Python.

Per fare ciò, cambia questo:

from application.app.folder.file import func_name

a questo:

from .application.app.folder.file import func_name

Aggiungendo il punto che stai dicendo cerca in questa cartella la cartella dell'applicazione invece di cercare nella directory Python.


2
questo è ciò che mi ha risolto
Phi

32

Da quello che so, aggiungere un __init__.pyfile direttamente nella cartella delle funzioni che si desidera importare farà il lavoro.


7
solo se lo script che vuole includere quell'altra directory è già nel sys.path
Ax3l

2
Ho usato sys.path.append(tools_dir)su Windows e non ho bisogno di aggiungere un __init__.py' file in my directory tools_dir`
herve-guerin

25

In Python 3.4 e versioni successive, è possibile importare direttamente da un file di origine (collegamento alla documentazione) . Questa non è la soluzione più semplice, ma sto includendo questa risposta per completezza.

Ecco un esempio Innanzitutto, il file da importare, denominato foo.py:

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

Il codice che importa il file sopra, fortemente ispirato all'esempio nella documentazione:

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()

Il risultato:

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

Si noti che il nome della variabile, il nome del modulo e il nome file non devono necessariamente corrispondere. Questo codice funziona ancora:

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()

Il risultato:

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

L'importazione a livello di codice dei moduli è stata introdotta in Python 3.1 e offre un maggiore controllo su come i moduli vengono importati. Fare riferimento alla documentazione per ulteriori informazioni.


11
Non so se qualcuno abbia mai provato a capirlo, ma penso che sia troppo complicato.
Dan

23

Ha funzionato per me in python3 su linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  

6
sys.path.append("/home/linux/folder/")- Assicurati di non utilizzare un collegamento, ad esempio"~/folder/"
daGo

22

Prova le importazioni relative di Python:

from ...app.folder.file import func_name

Ogni punto iniziale è un altro livello superiore nella gerarchia che inizia con la directory corrente.


I problemi? Se questo non funziona per te, probabilmente stai diventando morso dalle molte importazioni relative di Gotcha. Leggi le risposte e i commenti per maggiori dettagli: Come risolvere "Tentativo di importazione relativa in non pacchetto" anche con __init__.py

Suggerimento: avere __init__.pya ogni livello di directory. Potrebbe essere necessario python -m application.app2.some_folder.some_file(tralasciando .py) che si esegue dalla directory di livello superiore o che si ha quella directory di livello superiore nel proprio PYTHONPATH. Accidenti!


22

Ho dovuto affrontare la stessa sfida, soprattutto durante l'importazione di più file, ecco come sono riuscito a superarlo.

import os, sys

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

from root_folder import file_name

2
La tua risposta sarebbe più utile se potessi spiegare cosa fa diversamente da una normale importazione?
not2qubit

1
Ho avuto /path/dir1/__init__.py e /path/dir1/mod.py. Per /path/some.py dalla funzione di importazione dir1.mod ha funzionato. Quando in /path/dir2/some.py ha funzionato solo dopo aver copiato e incollato la risposta sopra nella parte superiore del file. Non volevo modificare il mio percorso poiché non tutti i progetti Python che ho sono in / path /.
jwal

19

Considerando applicationcome la directory principale per il progetto di pitone, creare un vuoto __init__.pyfile in application, appe folderle cartelle. Quindi, some_file.pyapportare le modifiche come segue per ottenere la definizione di func_name:

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()

dovrebbe essere: sys.path.insert (0, r '/ da / root / directory')
Bolaka,

18

L'uso di sys.path.append con un percorso assoluto non è l'ideale quando si sposta l'applicazione in altri ambienti. L'uso di un percorso relativo non funzionerà sempre perché la directory di lavoro corrente dipende da come è stato invocato lo script.

Poiché la struttura delle cartelle dell'applicazione è fissa, possiamo usare os.path per ottenere il percorso completo del modulo che vogliamo importare. Ad esempio, se questa è la struttura:

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

E diciamo che vuoi importare il modulo "mango". In vanilla.py potresti fare quanto segue:

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

Ovviamente, non hai bisogno della variabile mango_dir.

Per capire come funziona questo esempio di sessione interattiva:

>>> 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'
>>> 

E controlla la documentazione di os.path .


13

Questo funziona per me su Windows

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

import some_file

10

Sono abbastanza speciale: uso Python con Windows!

Completo solo le informazioni: sia per Windows che per Linux, funzionano sia il percorso relativo sia quello assoluto sys.path(ho bisogno di percorsi relativi perché utilizzo i miei script sui vari PC e in diverse directory principali).

E quando si usano entrambi Windows \e /possono essere usati come separatore per i nomi dei file e ovviamente è necessario raddoppiare le \stringhe Python,
alcuni esempi validi:

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

(nota: penso che /sia più conveniente di \, evento se è meno 'nativo di Windows' perché è compatibile con Linux e più semplice da scrivere e copiare in Windows Explorer)


4
os.path.join ('tools', 'mydir')
Corey Goldberg

7

Se lo scopo di caricare un modulo da un percorso specifico è di aiutarti durante lo sviluppo di un modulo personalizzato, puoi creare un collegamento simbolico nella stessa cartella dello script di test che punta alla radice del modulo personalizzato. Questo riferimento al modulo avrà la precedenza su qualsiasi altro modulo installato con lo stesso nome per qualsiasi script eseguito in quella cartella.

Ho provato questo su Linux, ma dovrebbe funzionare in qualsiasi sistema operativo moderno che supporti i collegamenti simbolici.

Un vantaggio di questo approccio è che puoi indicare un modulo che si trova nella tua copia di lavoro della filiale SVC locale che può semplificare notevolmente il tempo del ciclo di sviluppo e ridurre le modalità di errore nella gestione di diverse versioni del modulo.


7

Nel mio caso avevo una classe da importare. Il mio file era simile al seguente:

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

Nel mio file principale ho incluso il codice tramite:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper

1
@ not2qubit sys non è stato importato nella risposta.
Walter,

7

Ho incontrato più volte la stessa domanda, quindi vorrei condividere la mia soluzione.

Versione Python: 3.X

La seguente soluzione è per chi sviluppa la tua applicazione in Python versione 3.X perché Python 2 non è supportato dal 1 gennaio 2020 .

Struttura del progetto

In python 3, non è necessario __init__.pynella sottodirectory del progetto a causa dei pacchetti dello spazio dei nomi impliciti . See Is init non .py richiesto per i pacchetti in Python 3.3+

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

Dichiarazione problema

In file_b.py, vorrei importare una classe Ainfile_a.py cartella a.

soluzioni

# 1 Un modo rapido ma sporco

Senza installare il pacchetto come te, stai attualmente sviluppando un nuovo progetto

Utilizzando il try catchper verificare se gli errori. Esempio di codice:

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 Installa il tuo pacchetto

Una volta installata l'applicazione (in questo post, il tutorial di installazione non è incluso)

Puoi semplicemente

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")

Buona programmazione!


per maggiori informazioni sulle importazioni assolute
WY Hsu

3

Stavo lavorando al progetto ache volevo che gli utenti installassero pip install acon il seguente elenco di file:

.
├── 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

un / 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

Ho installato il modulo eseguendo quanto segue dalla directory con MANIFEST.in:

python setup.py install

Quindi, da una posizione totalmente diversa sul mio filesystem /moustache/armwrestlesono stato in grado di eseguire:

import a
dir(a)

Ciò ha confermato che in a.catseffetti era uguale a 0 e in a.b.dogseffetti uguale a 1, come previsto.


2

Invece di limitarti a fare un import ..., fai questo:

from <MySubFolder> import <MyFile>

MyFile è all'interno di MySubFolder.


1

Puoi aggiornare la shell di Python premendo f5 o vai su Esegui> Esegui modulo. In questo modo non è necessario modificare la directory per leggere qualcosa dal file. Python cambierà automaticamente la directory. Ma se vuoi lavorare con file diversi da directory diverse in Python Shell, puoi cambiare la directory in sys, come ha detto Cameron in precedenza.


1

Quindi avevo appena fatto clic con il pulsante destro del mouse sul mio IDE e ne avevo aggiunto uno nuovo foldere mi chiedevo perché non ero in grado di importarlo. Successivamente ho capito che dovevo fare clic con il tasto destro e creare un pacchetto Python e non una classica cartella del file system. Oppure un metodo post mortem che aggiunge un __init__.py(che fa in modo che Python tratti la cartella del file system come un pacchetto) come indicato in altre risposte. Aggiungendo questa risposta qui nel caso in cui qualcuno abbia seguito questa strada.


1

Puoi usare importlib per importare moduli in cui vuoi importare un modulo da una cartella usando una stringa in questo modo:

import importlib

scriptName = 'Snake'

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

Questo esempio ha un main.py che è il codice sopra, quindi una cartella chiamata Scripts e quindi puoi chiamare qualunque cosa ti serva da questa cartella cambiando la scriptNamevariabile. È quindi possibile utilizzare scriptper fare riferimento a questo modulo. come se avessi una funzione chiamata Hello()nel modulo Snake puoi eseguire questa funzione in questo modo:

script.Hello()

Ho provato questo in Python 3.6


0

Ho avuto questi problemi diverse volte. Sono venuto su questa stessa pagina molto. Nel mio ultimo problema ho dovuto eseguire il serverda una directory fissa, ma ogni volta che debug volevo eseguire da diverse sottodirectory.

import sys
sys.insert(1, /path) 

ha NON lavorare per me, perché in diversi moduli ho dovuto leggere diversi * .csv file che erano tutti nella stessa directory.

Alla fine, ciò che ha funzionato per me non era pitonico, immagino, ma:

Ho usato un if __main__ modulo in cima al quale volevo eseguire il debug , che viene eseguito da un percorso diverso dal solito.

Così:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.