Importa un modulo da un percorso relativo


759

Come importare un modulo Python dato il suo percorso relativo?

Ad esempio, se dirFoocontiene Foo.pye dirBar, e dirBarcontiene Bar.py, come faccio a importare Bar.pyin Foo.py?

Ecco una rappresentazione visiva:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foodesidera includere Bar, ma la ristrutturazione della gerarchia di cartelle non è un'opzione.



3
Controlla la mia risposta, è la più completa finora, altri non funzionano in casi speciali, ad esempio quando chiami lo script da un'altra directory o da un altro script Python. Vedi stackoverflow.com/questions/279237/…
sorin

Ho avuto un problema simile e ho trovato questo e funziona !! apt-get install python-profiler
ldcl289

5
Nel caso in cui qualcuno voglia farlo staticamente e arrivi qui (come ho fatto io :) puoi anche impostare la variabile d'ambiente PYTHONPATH
okaram

Meglio seguire le istruzioni in Lib / site.py per ogni caso
Avenida Gez

Risposte:


333

Supponendo che entrambe le directory siano veri pacchetti Python (hanno il __init__.pyfile al loro interno), ecco una soluzione sicura per l'inclusione dei moduli relativamente alla posizione dello script.

Presumo che tu voglia farlo, perché devi includere un set di moduli con il tuo script. Lo uso in produzione in diversi prodotti e funziona in molti scenari speciali come: script chiamati da un'altra directory o eseguiti con python execute invece di aprire un nuovo interprete.

 import os, sys, inspect
 # realpath() will make your script run, even if you symlink it :)
 cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
 if cmd_folder not in sys.path:
     sys.path.insert(0, cmd_folder)

 # Use this if you want to include modules from a subfolder
 cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
 if cmd_subfolder not in sys.path:
     sys.path.insert(0, cmd_subfolder)

 # Info:
 # cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
 # __file__ fails if the script is called in different ways on Windows.
 # __file__ fails if someone does os.chdir() before.
 # sys.argv[0] also fails, because it doesn't not always contains the path.

Come bonus, questo approccio ti consente di forzare Python a usare il tuo modulo invece di quelli installati sul sistema.

Avvertimento! Non so davvero cosa sta succedendo quando il modulo corrente è all'interno di un eggfile. Probabilmente fallisce anche questo.


5
posso avere una spiegazione su come funziona? Ho un problema simile e mi piacerebbe forzare un modulo Python DIR invece di eseguire una ricerca
Carl Crott,

Con Win 7 Pro 64x e Python 2.7 ottengo alcuni errori. 1) Ho dovuto aggiungere ispeziona alla lista di importazione. 2) Il 1o valore, [0], nella tupla è una stringa vuota. Il secondo, [1], mostra il nome del file. Immagino che il primo dovrebbe essere il percorso ... Qualche idea?
Adam Lewis,

2
Se si sta andando per una sottocartella, usare in questo modo: os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")Do NON aggiungere la sottocartella prima di abspathquanto questo provoca gravi bug.
Maximilian Hils,

4
@ scr4ve invece dovresti usare os.path.join () e puoi aggiungere il caso ( cmd_subfolder) direttamente alla mia risposta. Grazie!
sorin,

7
per me realpathgenera già percorsi assoluti, quindi non ne ho bisogno abspath. Inoltre os.path.dirnamepuò essere utilizzato invece di dividere, rendendo [0]obsoleta l'indicizzazione . La linea sarebbe quindi:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
mer

328

Assicurati che dirBar abbia il __init__.pyfile: questo trasforma una directory in un pacchetto Python.


204
Si noti che questo file può essere completamente vuoto.
Harley Holcombe,

47
Se la directory principale di dirBar non è presente, sys.pathla presenza di __init__.pynella dirBardirectory non aiuta molto.
jfs,

7
-1, l'aggiunta __init.py__funzionerà solo quando la directory è già in sys.path e nel mio caso non lo era. La soluzione di "sorin" (accettata) funziona sempre.
Czarek Tomczak,

12
"quando la directory è già in sys.path". Sebbene sia del tutto vero, come possiamo immaginare che la directory non fosse presente nella sys.pathdomanda? Forse c'è stato qualcosa di omesso che non abbiamo visto o conosciuto?
S. Lott,

4
Questo non significa assolutamente la risposta alla domanda: se il file di inizializzazione è presente o meno, questo non fa apparire Python nelle sottodirectory. Come ha ottenuto centinaia di voti?
gented

261

Puoi anche aggiungere la sottodirectory al tuo percorso Python in modo che venga importata come uno script normale.

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar

6
Sembra che la tua risposta non funzioni con i percorsi relativi, vedi stackoverflow.com/questions/279237/…
bogdan,

24
Questo fa il lavoro con i percorsi relativi - è sufficiente per capire che un percorso relativo dipenderà da quello che directory si esegue da, il che rende questo una soluzione povero per qualcosa di diverso da un trucco veloce.
nobar

12
Puoi fare qualcosa del generesys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko,

osys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim

1
Prendi in considerazione l'utilizzo sys.path.insert(0, <path to dirFoo>)in quanto caricherà questo modulo prima dei moduli con lo stesso nome memorizzati altrove.
Anton Tarasenko,

117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

2
Mi piace perché hai la flessibilità di andare su una directory.
Charles L.

21
Dovresti usare os.path.join()invece di unirti con '/', che si romperà nelle finestre (zoppo).
0xc0de

18
Questo non è affidabile. Dipende da qualunque sia l'attuale directory di lavoro, non dalla directory in cui si trova lo script.
jamesdlin,

Potrebbe voler aggiungere solo se il percorso non è già nel percorso. lib_path = os.path.abspath ('../ Functions') se lib_path non è in sys.path: sys.path.append (lib_path)
Brent,

in risposta a @jamesdlin combinando alcune risposte: che dire os.path.abspath(os.path.join(__file__,'..','lib'))?
Matthew Davis,

107

Basta fare cose semplici per importare il file .py da una cartella diversa.

Diciamo che hai una directory come:

lib/abc.py

Quindi tieni un file vuoto nella cartella lib come indicato

__init__.py

E poi usa

from lib.abc import <Your Module name>

Conserva il __init__.pyfile in ogni cartella della gerarchia del modulo di importazione.


79

Se strutturi il tuo progetto in questo modo:

src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

Quindi da Foo.py dovresti essere in grado di fare:

import dirFoo.Foo

O:

from dirFoo.Foo import FooObject

Secondo il commento di Tom, ciò richiede che la srccartella sia accessibile tramite site_packagesil percorso di ricerca. Inoltre, come accenna, __init__.pyviene implicitamente importato quando si importa per la prima volta un modulo in quel pacchetto / directory. In genere __init__.pyè semplicemente un file vuoto.


Si noti inoltre che init .py viene importato quando viene importato il primo modulo all'interno di quel pacchetto. Inoltre, il tuo esempio funzionerà solo se src è nel site_packages (o nel percorso di ricerca)
Tom Leys,

3
Questa è la soluzione più semplice che stavo cercando. Se il file da importare è sicuro di trovarsi in una delle sottodirectory, questa soluzione è una gemma.
Deepak GM del

Ho provato la stessa cosa e ho fallito. Non so perché. ImportError: nessun modulo chiamato customMath
Ming Li

9
Perché qualcuno dovrebbe voler importare Foo dall'interno di Foo.py stesso. Dovrei provenire da Bar.py, immagino.
bhaskarc,

from dirFoo import Fooda dire Foo.bla(). Se si usa import dirFoo.Foouno dovrebbe usare dirFoo.Foo.bla()- piuttosto brutto anche senza custodia per cammello.
Cees Timmerman,

45

Il metodo più semplice è usare sys.path.append ().

Tuttavia, potresti essere interessato anche al modulo imp . Fornisce accesso alle funzioni di importazione interne.

# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file 

Questo può essere usato per caricare i moduli in modo dinamico quando non si conosce il nome di un modulo.

L'ho usato in passato per creare un'interfaccia di tipo plug-in per un'applicazione, in cui l'utente avrebbe scritto uno script con funzioni specifiche dell'applicazione e avrebbe semplicemente lasciato cadere il proprio script in una directory specifica.

Inoltre, queste funzioni possono essere utili:

imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)

Si noti che nella documentazione imp.load_source e imp.load_compiled sono elencati come obsoleti. Si consiglia invece imp.find_module e imp.load_module.
amicitas,

@amicitas, puoi per favore fornire qualche riferimento per questo (ne ho bisogno e sto usando python 2.6. Sono consapevole di cosa dicono 2.7 documenti su questi, ma non sono riuscito a trovare alcun riferimento riguardo a 2.6)
0xc0de

@ 0xc0de Puoi trovare quell'istruzione nei documenti per il modulo imp sia per python 2.7.3 che per python 2.6.7 . Sembra che quelle funzioni non siano nemmeno nei documenti per Python 3.2.
amicitas,

1
Per importare file sorgente come questo in Python 3.3, vedere Importare file sorgente Python abitrary. (Python 3.3+) - Stack Overflow . Grazie, gente per i suggerimenti qui.
nealmcb,


23

Il modo più semplice senza alcuna modifica allo script è impostare la variabile di ambiente PYTHONPATH. Perché sys.path è inizializzato da queste posizioni:

  1. La directory contenente lo script di input (o la directory corrente).
  2. PYTHONPATH (un elenco di nomi di directory, con la stessa sintassi della variabile shell PATH).
  3. L'impostazione predefinita dipende dall'installazione.

Corri:

export PYTHONPATH=/absolute/path/to/your/module

Il file sys.path contiene il percorso sopra, come mostrato di seguito:

print sys.path

['', '/absolute/path/to/your/module', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client', '/usr/lib/python2.7/dist-packages/ubuntuone-client', '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel', '/usr/lib/python2.7/dist-packages/ubuntuone-couch', '/usr/lib/python2.7/dist-packages/ubuntuone-installer', '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']

13

Secondo me la scelta migliore è quella di mettere __ init __.py nella cartella e chiamare il file con

from dirBar.Bar import *

Non è consigliabile utilizzare sys.path.append () perché qualcosa potrebbe andare storto se si utilizza lo stesso nome file del pacchetto python esistente. Non l'ho provato ma sarà ambiguo.


strano che from dirBar.Bar import *funzioni, ma non from dirBar.Bar import Bar. sai perché * funziona? e se avessi più file in dirBar / e volessi prenderne solo alcuni (usando un metodo come quello che hai pubblicato qui)?
tester

2
@tester: utilizzare from dirBar import Bar.
nobar

@tester è perché fromindica la fonte, e tutto importciò che segue è cosa prendere da quella fonte. from dirBar.Bar import Barsignifica "Dalla fonte, importa la fonte stessa", il che non ha senso. i *però significa, "mi danno tutto, dalla sorgente"
FuriousFolder

11

Il modo più semplice per gli utenti Linux

Se stai solo armeggiando e non ti preoccupi dei problemi di distribuzione, puoi usare un collegamento simbolico (supponendo che il tuo filesystem lo supporti) per rendere il modulo o il pacchetto direttamente visibile nella cartella del modulo richiedente.

ln -s (path)/module_name.py

o

ln -s (path)/package_name

Nota: un "modulo" è qualsiasi file con estensione .py e un "pacchetto" è qualsiasi cartella che contiene il file __init__.py(che può essere un file vuoto). Dal punto di vista dell'utilizzo, moduli e pacchetti sono identici: entrambi espongono le loro "definizioni e istruzioni" contenute come richiesto tramite il importcomando.

Vedi: http://docs.python.org/2/tutorial/modules.html


10
from .dirBar import Bar

invece di:

from dirBar import Bar

nel caso in cui ci fosse un altro dirBar installato e confondere un lettore foo.py.


2
Non sono stato in grado di farlo su Windows. È su Linux?
Gabriel,

1
Funzionerà da uno script che importa Foo. vale a dire: main.py importa dirFoo.Foo. Se stai tentando di eseguire Foo.py come script, fallirà. Vedere stackoverflow.com/questions/72852/...
jgomo3

9

Per questo caso, per importare Bar.py in Foo.py, prima trasformerei queste cartelle in pacchetti Python in questo modo:

dirFoo\
    __init__.py
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Quindi lo farei così in Foo.py:

from .dirBar import Bar

Se volessi che lo spazio dei nomi fosse Bar. qualunque cosa , o

from . import dirBar

Se volessi il dirBar.Bar di namespace. qualunque cosa . Questo secondo caso è utile se si hanno più moduli nel pacchetto dirBar.


7

Aggiungi un file __init__.py :

dirFoo\
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Quindi aggiungi questo codice all'inizio di Foo.py:

import sys
sys.path.append('dirBar')
import Bar

5
Se dirBarè già un pacchetto Python (per l'esistenza di dirBar/__init__.py), non è necessario aggiungere dirBara sys.path, no? La dichiarazione import Bardi Foo.pydovrebbe essere sufficiente.
Santa,

5

Esempio di sys.path relativo:

# /lib/my_module.py
# /src/test.py


if __name__ == '__main__' and __package__ is None:
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module

Sulla base di questa risposta.


5

Bene, come dici tu, di solito vuoi avere accesso a una cartella con i tuoi moduli relativamente a dove viene eseguito il tuo script principale, quindi li devi solo importare.

Soluzione:

Ho lo script in D:/Books/MyBooks.pye alcuni moduli (come oldies.py). Devo importare dalla sottodirectory D:/Books/includes:

import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path)  # Just verify it is there
import oldies

Inserisci un print('done')in oldies.py, in modo da verificare che tutto vada bene. In questo modo funziona sempre perché dalla definizione di Python sys.pathinizializzata all'avvio del programma, il primo elemento di questo elenco,path[0] , è la directory contenente lo script che è stato usato per invocare l'interprete Python.

Se la directory di script non è disponibile (ad es. Se l'interprete viene richiamato in modo interattivo o se lo script viene letto dall'input standard), path[0]è la stringa vuota che indirizza Python a cercare prima i moduli nella directory corrente. Si noti che la directory di script viene inserita prima delle voci inserite come risultato di PYTHONPATH.


1
Ho dovuto usare una barra rovesciata anziché due barre rovesciate (ovvero site.addsitedir(sys.path[0]+'/includes')) nel mio primo programma Python semplice break_time.py: https://github.com/ltfschoen/PythonTest . Uso il sistema: MacOS v10.11.5, Python 2.7.12, IDLE IDE 2.7.12, Tk 8.5.9
Luke Schoen,

4

Semplicemente puoi usare: from Desktop.filename import something

Esempio:

dato che il file è nome test.pynella directory Users/user/Desktope importa tutto.

il codice:

from Desktop.test import *

Ma assicurati di creare un file vuoto chiamato " __init__.py" in quella directory


1
le importazioni speciali sono sconsigliate. vedi: stackoverflow.com/questions/2386714/why-is-import-bad
axolotl

so che è per questo che ho scritto per la prima volta import somethingpoi ho detto di renderlo più semplice in * pratica non è buono per ram e anche se 2 funzioni hanno lo stesso nome si
accumulerà il

3

Un'altra soluzione sarebbe quella di installare il pacchetto py-request e quindi utilizzare quanto segue inFoo.py

import require
Bar = require('./dirBar/Bar')

L'URL in realtà sembra essere pypi.org/project/require.py e nota che deve essere installato tramite Pip.
Flash Sheridan,

1
@FlashSheridan No, è un progetto diverso. Ho eliminato py-request dato che ero sicuro che nessuno lo stesse usando, ma non ho pensato a questo post. Se hai ancora bisogno di una require()funzione, puoi dare un'occhiata al mio progetto Node.py: github.com/nodepy/nodepy
Niklas R

2

Ecco un modo per importare un file da un livello sopra, usando il percorso relativo.

Fondamentalmente, basta spostare la directory di lavoro su un livello (o qualsiasi posizione relativa), aggiungerla al percorso, quindi spostare la directory di lavoro nel punto in cui è iniziata.

#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path =  os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)

1
non capisco la tua logica qui. è troppo complicato
transilvlad,

0

Non ho esperienza di Python, quindi se c'è qualcosa di sbagliato nelle mie parole, dimmelo. Se la gerarchia dei file è disposta in questo modo:

project\
    module_1.py 
    module_2.py

module_1.pydefinisce una funzione chiamata func_1(), module_2.py :

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

e corri python module_2.pyin cmd, eseguirà ciò che func_1()definisce. Di solito è così che importiamo gli stessi file della gerarchia. Ma quando si scrive from .module_1 import func_1in module_2.py, interprete Python dirà No module named '__main__.module_1'; '__main__' is not a package. Quindi, per risolvere questo problema, manteniamo solo le modifiche che facciamo, spostiamo entrambi i moduli in un pacchetto e facciamo un terzo modulo come chiamante da eseguire module_2.py.

project\
    package_1\
        module_1.py
        module_2.py
    main.py

main.py :

from package_1.module_2 import func_2

def func_3():
    func_2()

if __name__ == '__main__':
    func_3()

Ma la ragione si aggiunge una .prima module_1a module_2.pyè che se non lo facciamo ed eseguire main.py, interprete Python dirà No module named 'module_1', che è un po 'difficile, module_1.pyè proprio accanto module_2.py. Ora lascio func_1()a module_1.pyfare qualcosa:

def func_1():
    print(__name__)

che __name__registra chi chiama func_1. Ora manteniamo il .precedente module_1, eseguiamo main.py, stamperà package_1.module_1, no module_1. Indica che colui che chiama func_1()è nella stessa gerarchia di main.py, .implica che module_1è nella stessa gerarchia di se module_2.pystesso. Quindi se non c'è un punto, main.pyriconoscerà module_1nella stessa gerarchia di se stesso, può riconoscere package_1, ma non cosa "sotto".

Ora rendiamolo un po 'complicato. Hai un config.inie un modulo definisce una funzione per leggerlo nella stessa gerarchia di 'main.py'.

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
    main.py

E per qualche motivo inevitabile, devi chiamarlo con module_2.py, quindi deve importare dalla gerarchia superiore. module_2.py :

 import ..config
 pass

Due punti indicano l'importazione dalla gerarchia superiore (tre punti accedono alla parte superiore della parte superiore e così via). Ora corriamo main.py, l'interprete dirà: ValueError:attempted relative import beyond top-level package. Il "pacchetto di livello superiore" qui è main.py. Solo perché config.pyè accanto main.py, sono nella stessa gerarchia, config.pynon sono "sotto" main.py, o non sono "guidati" da main.py, quindi è oltre main.py. Per risolvere questo problema, il modo più semplice è:

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
main.py

Penso che coincida con il principio di organizzare la gerarchia dei file di progetto, dovresti disporre i moduli con funzioni diverse in cartelle diverse e lasciare un chiamante superiore all'esterno e puoi importare quanto vuoi.


-5

Funziona anche ed è molto più semplice di qualsiasi altra cosa con sys modulo:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())

1
Se OP stava per codificare un percorso, potrebbero comunque inserire quel percorso nel PYTHONPATH. Penso che il punto sia farlo in un modo che non codifica un percorso poiché si spezzerebbe da nessun'altra parte.
Matteo,

-15

Chiamami eccessivamente cauto, ma mi piace rendere il mio più portatile perché non è sicuro supporre che i file saranno sempre nello stesso posto su ogni computer. Personalmente ho il codice cercare prima il percorso del file. Uso Linux quindi il mio sarebbe simile al seguente:

import os, sys
from subprocess import Popen, PIPE
try:
    path = Popen("find / -name 'file' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
    if not sys.path.__contains__(path):
        sys.path.append(path)
except IndexError:
    raise RuntimeError("You must have FILE to run this program!")

Ovviamente, a meno che non preveda di raggrupparli insieme. Ma in tal caso, in realtà non sono necessari due file separati.


11
Super inefficiente!
0xc0de

1
I file saranno sempre nello stesso posto su ogni computer, dato un percorso relativo.
Sagar Hatekar,
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.