Importazione di moduli dalla cartella principale


627

Sto eseguendo Python 2.5.

Questo è il mio albero delle cartelle:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(Ho anche __init__.pyin ogni cartella, omesso qui per leggibilità)

Come importare il nibmodulo dall'interno del lifemodulo? Spero sia possibile fare a meno di armeggiare con sys.path.

Nota: il modulo principale in esecuzione si trova nella ptdraftcartella.


1
Qual è la tua impostazione PYTHONPATH?
S.Lott

2
Ross: Ho guardato lì. Cosa dovrei fare al riguardo? Ho già un __init__.py. S.Lott: Non so come controllare ...
Ram Rachum,

4
echo $ PYTHONPATH dalla shell; import sys; stampa sys.path dall'interno di Python. docs.python.org/tutorial/…
S.Lott

@FlipMcF Google è un motore di ricerca gorgogliante, quindi il fatto che questo risultato sia abbastanza alto per te non importa. Molto più importante è il fatto che anche il motore di ricerca non gorgogliante, DuckDuckGo, lo classifica molto.
ArtOfWarfare

3
Consiglio vivamente di saltare tutte sys.patho tutte le PYTHONPATHrisposte e di dare un'occhiata all'ottima risposta di np8 . Sì, è una lettura lunga. Sì, sembra un sacco di lavoro. Ma è l'unica risposta che risolve effettivamente il problema in modo corretto e pulito.
Aran-Fey,

Risposte:


119

Sembra che il problema non sia correlato al fatto che il modulo si trova in una directory padre o qualcosa del genere.

Devi aggiungere la directory che contiene ptdrafta PYTHONPATH

Hai detto che ha import nibfunzionato con te, probabilmente significa che hai aggiunto ptdraftse stesso (non il suo genitore) a PYTHONPATH.


15
Dover aggiungere tutto a pythonpath non è una buona soluzione se stai lavorando con molti pacchetti che non verranno mai riutilizzati.
Jason Cheng,

@JasonCheng: Allora perché sono pacchetti ?
Davis Herring,

7
Per me questa risposta ha funzionato. Tuttavia, per renderlo più esplicito, la procedura è import syse quindisys.path.append("..\<parent_folder>")
BCJuan

604

È possibile utilizzare le importazioni relative (python> = 2.5):

from ... import nib

(Novità di Python 2.5) PEP 328: importazioni assolute e relative

EDIT : aggiunto un altro punto '.' per salire due pacchi


178
ValueError: Attempted relative import in non-package
endolito il

18
@endolith: devi essere in un pacchetto, cioè devi avere un file init .py.
Kirbyfan64sos,

31
Tentativo di importazione relativa oltre il pacchetto di livello superiore
Utente

291
Per essere ancora più precisi, è necessario un __init__.pyfile.
Kara Deniz,

17
Vedi anche la seguente risposta, poiché l'aggiunta __init__.pynon è l'unica cosa che devi fare: stackoverflow.com/questions/11536764/…
Ben Farmer,

286

Le importazioni relative (come in from .. import mymodule) funzionano solo in un pacchetto. Per importare "mymodule" che si trova nella directory principale del modulo corrente:

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

modifica : l' __file__attributo non viene sempre assegnato. Invece di utilizzare os.path.abspath(__file__)ora ho suggerito di utilizzare il modulo inspect per recuperare il nome file (e il percorso) del file corrente


97
qualcosa di un po 'più corto:sys.path.insert(1, os.path.join(sys.path[0], '..'))
JHolta il

8
Qualche motivo per evitare sys.path.append () invece dell'inserimento?
Tyler,

2
@Tyler - Può importare se da qualche altra parte poi parentdir, ma in uno dei percorsi già specificati in sys.path, c'è un altro modulo con il nome 'mymodule'. L'inserimento parentdircome primo elemento sys.pathdell'elenco assicura che il modulo da parentdirverrà invece importato.
Remi,

5
@JHolta Se non hai a che fare con i pacchetti, il tuo è la soluzione migliore.
Jonathon Reinhart,

5
@JHolta ottima idea. sys.path.insert(1, os.path.realpath(os.path.pardir))funziona anche.
SpeedCoder5

179

Ho pubblicato una risposta simile anche alla domanda relativa alle importazioni da pacchetti di pari livello. Puoi vederlo qui .

Soluzione senza sys.pathhack

Sommario

  • Avvolgere il codice in una cartella (ad es. packaged_stuff)
  • Usa lo setup.pyscript di creazione in cui usi setuptools.setup () .
  • Pip installa il pacchetto in stato modificabile con pip install -e <myproject_folder>
  • Importa utilizzando from packaged_stuff.modulename import function_name

Impostare

Presumo la stessa struttura di cartelle della domanda

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

Chiamo la .cartella principale e nel mio caso si trova in C:\tmp\test_imports.

passi

1) Aggiungi setup.pya alla cartella principale

Il contenuto di setup.pypuò essere semplicemente

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

Fondamentalmente "qualsiasi" setup.pyavrebbe funzionato. Questo è solo un esempio minimo di lavoro.

2) Utilizzare un ambiente virtuale

Se hai familiarità con gli ambienti virtuali, attivane uno e vai al passaggio successivo. L'uso di ambienti virtuali non è assolutamente necessario, ma ti aiuteranno davvero a lungo termine (quando hai in corso più di 1 progetto ...). I passaggi più basilari sono (esegui nella cartella principale)

  • Crea un ambiente virtuale
    • python -m venv venv
  • Attiva env virtuale
    • . /venv/bin/activate(Linux) o ./venv/Scripts/activate(Win)

Per saperne di più su questo, basta Google "tutorial Python virtualenv" o simili. Probabilmente non avrai mai bisogno di altri comandi oltre a creare, attivare e disattivare.

Dopo aver creato e attivato un ambiente virtuale, la console dovrebbe fornire il nome dell'ambiente virtuale tra parentesi

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3) pip installa il tuo progetto in stato modificabile

Installa il tuo pacchetto di livello superiore myprojectutilizzando pip. Il trucco è usare la -ebandiera durante l'installazione. In questo modo viene installato in uno stato modificabile e tutte le modifiche apportate ai file .py verranno automaticamente incluse nel pacchetto installato.

Nella directory principale, eseguire

pip install -e . (notare il punto, sta per "directory corrente")

Puoi anche vedere che è installato usando pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Importa anteponendo mainfolderad ogni importazione

In questo esempio, mainfoldersarebbe ptdraft. Ciò ha il vantaggio di non incorrere in collisioni di nomi con altri nomi di moduli (dalla libreria standard di Python o da moduli di terze parti).


Esempio di utilizzo

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

life.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

Running life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!

1
Perché funziona? Sta cambiando il PYTHONPATH dietro le quinte in qualche modo?
Homero Esmeraldo,

2
Inoltre, perché è meglio o più elegante dell'utilizzo dell'approccio sys.path?
Homero Esmeraldo,

5
@HomeroBarrocasSEsmeraldo Buone domande. PYTHONPATH env var non è toccato. Questo si installa nei pacchetti del sito , che è già attivo sys.pathe in cui il codice verrebbe normalmente installato dagli utenti finali. È meglio degli sys.pathhack (e puoi includere l'uso di PYTHONPATH in quella categoria di path hack) perché significa che il codice in sviluppo dovrebbe comportarsi allo stesso modo per te come per gli altri utenti. Al contrario, quando si utilizzano le modifiche al percorso, le istruzioni di importazione vengono risolte in modo diverso.
mercoledì

5
Questa è di gran lunga la migliore soluzione che abbia letto. Ciò crea un collegamento alla directory corrente in modo che tutti gli aggiornamenti al codice vengano riflessi direttamente nella directory di lavoro. Nel caso in cui sia necessario eliminare questa voce, eliminare manualmente il file egg e rimuovere la voce da easy-install in dist-package (site-pacchetti se si utilizza pip).
Rakshit Kothari,

2
perché rendere il mal di testa importare pitone ancora più complicato? Dobbiamo scrivere un file di installazione, un env virtuale, un'installazione pip? Oh mio Dio!
Emerson Xu

68

È possibile utilizzare il sistema operativo in base al percorso nel "percorso di ricerca del modulo" che è elencato in sys.path . Quindi puoi facilmente aggiungere la directory principale come segue

import sys
sys.path.insert(0,'..')

Se vuoi aggiungere la directory genitore-genitore,

sys.path.insert(0,'../..')

Funziona sia in Python 2 che 3.


6
Questo è relativo alla directory corrente , nemmeno necessariamente quella contenente lo script principale.
Davis Herring,

57

Se l'aggiunta della cartella del modulo a PYTHONPATH non ha funzionato, è possibile modificare l' elenco sys.path nel programma in cui l'interprete Python cerca i moduli da importare, la documentazione di Python dice:

Quando viene importato un modulo chiamato spam , l'interprete cerca prima un modulo integrato con quel nome. Se non lo trova, cerca un file chiamato spam.py in un elenco di directory fornito dalla variabile sys.path. sys.path è inizializzato da queste posizioni:

  • la directory contenente lo script di input (o la directory corrente).
  • PYTHONPATH (un elenco di nomi di directory, con la stessa sintassi della variabile shell PATH).
  • l'impostazione predefinita dipendente dall'installazione.

Dopo l'inizializzazione, i programmi Python possono modificare sys.path . La directory contenente lo script in esecuzione viene posizionata all'inizio del percorso di ricerca, davanti al percorso della libreria standard. Ciò significa che verranno caricati gli script in quella directory anziché i moduli con lo stesso nome nella directory della libreria. Questo è un errore a meno che non sia prevista la sostituzione.

Sapendo questo, puoi fare quanto segue nel tuo programma:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft

6
La tua risposta è buona, ma potrebbe non funzionare sempre e non è molto portatile. Se il programma è stato spostato in una nuova posizione, /path/to/ptdraftdovrebbe essere modificato. Esistono soluzioni che elaborano la directory corrente del file e lo importano anche dalla cartella principale.
Edward

@Edward quali sono quelle tecniche?
Shuklaswag,

1
@Shuklaswag, ad esempio, la risposta stackoverflow.com/questions/714063/… .
Edward,

50

Non so molto di Python 2.
In Python 3, la cartella padre può essere aggiunta come segue:

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

... e quindi si è in grado di importare moduli da esso


13
Funziona solo se la tua attuale directory di lavoro è tale da '..'portare alla directory con il modulo in questione.
user5359531

31

Ecco una risposta semplice in modo da poter vedere come funziona, piccola e multipiattaforma.
Esso utilizza solo incorporato moduli ( os, syse inspect) così dovrebbe funzionare
su qualsiasi sistema operativo (OS) perché Python è progettato per questo.

Codice più breve per la risposta: meno righe e variabili

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

Per meno righe di questa, sostituire la seconda riga con import os.path as path, sys, inspect,
aggiungere inspect.all'inizio di getsourcefile(riga 3) e rimuovere la prima riga.
- tuttavia, questo importa tutto il modulo, quindi potrebbe essere necessario più tempo, memoria e risorse.

Il codice per la mia risposta ( versione più lunga )

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

Usa un esempio da una risposta Stack Overflow Come posso ottenere il percorso del
file attualmente eseguito in Python?
per trovare l'origine (nome file) del codice in esecuzione con uno strumento integrato.

from inspect import getsourcefile  
from os.path import abspath  

Quindi, ovunque tu voglia trovare il file sorgente da te, basta usare:

abspath(getsourcefile(lambda:0))

Il mio codice aggiunge un percorso di file a sys.path, l' elenco dei percorsi di Python
, perché questo permette Python per i moduli di importazione da quella cartella.

Dopo aver importato un modulo nel codice, è una buona idea eseguire sys.path.pop(0)su una nuova riga
quando quella cartella aggiunta ha un modulo con lo stesso nome di un altro modulo che viene importato
successivamente nel programma. È necessario rimuovere l'elemento dell'elenco aggiunto prima dell'importazione, non altri percorsi.
Se il tuo programma non importa altri moduli, è sicuro non eliminare il percorso del file perché
dopo la fine di un programma (o il riavvio della shell Python), tutte le modifiche apportate asys.path scompaiono.

Note su una variabile di nome file

La mia risposta non usa la __file__variabile per ottenere il percorso del file / nome file del
codice in esecuzione perché gli utenti qui lo hanno spesso descritto come inaffidabile . Non dovresti usarlo
per importare moduli dalla cartella genitore nei programmi usati da altre persone.

Alcuni esempi in cui non funziona (citazione da questa domanda Stack Overflow):

• non può essere trovato su alcune piattaforme • a volte non è il percorso completo del file

  • py2exenon ha un __file__attributo, ma esiste una soluzione alternativa
  • Quando si esegue da IDLE con execute()non esiste alcun __file__attributo
  • OS X 10.6 dove ottengo NameError: global name '__file__' is not defined

1
Ho fatto questo e rimosso la nuova voce del percorso sys.path.pop(0)immediatamente dopo aver importato il modulo desiderato. Tuttavia, le importazioni successive sono ancora andate in quel luogo. Ad esempio, ho app/config, app/toolse app/submodule/config. Da submodule, inserisco app/per importare tools, quindi rimuovo app/e provo a importare config, ma ottengo app/configinvece di app/submodule/config.
user5359531

Ho capito che una delle toolsimportazioni era anche importata configdalla directory principale. Quindi, quando in seguito ho provato a fare sys.path.pop(0); import configdentro submodule, aspettandomi di ottenere app/submodule/config, stavo effettivamente ottenendo app/config. Evidentemente Python restituisce una versione cache di un modulo con lo stesso nome invece di verificare effettivamente la presenza sys.pathdi un modulo corrispondente a quel nome. sys.pathin questo caso veniva modificato correttamente, Python non lo stava controllando a causa del modulo già omesso che era già stato caricato.
user5359531

Penso che questo sia il riferimento esatto al problema che ho avuto: docs.python.org/3/reference/import.html#the-module-cache
user5359531

Alcune informazioni utili da 5.3.1. La cache del modulo nella pagina della documentazione : Durante l'importazione, il nome del modulo viene cercato in sys.modules ... sys.modules è scrivibile. L'eliminazione di una chiave invaliderà la voce della cache per il modulo denominato, facendo in modo che Python esegua nuovamente la ricerca alla successiva importazione. ... Attenzione, come se si mantenga un riferimento all'oggetto modulo, si invalidi la sua cache e si reimportino, i due oggetti saranno diversi. Al contrario, importlib.reload()riutilizzeremo e reinizializzeranno il contenuto del modulo ...
Edward,

Ancora più breve: os.path.realpath(getsourcefile(lambda:0) + "/../.."). Verrà aggiunto il file di origine corrente con due simboli della directory principale. Non ho usato os.path.sepcome getsourcefileera restituisce una stringa utilizzando /anche su Windows. realpathsi occuperà di rimuovere due voci di directory dal percorso completo (in questo caso, il nome file e quindi la directory corrente) che fornisce la directory padre.
Coburn,

28

Ecco una soluzione più generica che include la directory principale in sys.path (funziona per me):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

import os, sys\n sys.path.insert(0,os.path.pardir) stessa cosa, ma più ordinata :) (nessuna riga nei commenti)
Anti Veeranna,

@antiveeranna, se usi non os.path.pardirotterrai il percorso reale del genitore, solo il percorso relativo da dove stai chiamandosys.path.insert()
alvas

1
Questa risposta usa la __file__variabile che può essere inaffidabile (non è sempre il percorso completo del file, non funziona su tutti i sistemi operativi ecc.) Come spesso menzionato dagli utenti StackOverflow. La modifica della risposta in modo da non includerla causerà meno problemi e sarà più compatibile tra loro. Per ulteriori informazioni, consultare stackoverflow.com/a/33532002/3787376 .
Edward,

23

Ho trovato il seguente modo funziona per importare un pacchetto dalla directory padre dello script. Nell'esempio, vorrei importare le funzioni env.pydal app.dbpacchetto.

.
└── my_application
    └── alembic
        └── env.py
    └── app
        ├── __init__.py
        └── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)

Quindi, un bel one-liner:sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
juzzlin,

La modifica sys.pathè di solito una cattiva idea. Hai bisogno di un modo ( ad es . PYTHONPATH) Affinché la tua libreria sia accessibile ad altro codice (oppure non è proprio una libreria); usalo sempre!
Davis Herring,

14

Anche le soluzioni sopra menzionate vanno bene. Un'altra soluzione a questo problema è

Se vuoi importare qualcosa dalla directory di livello superiore. Poi,

from ...module_name import *

Inoltre, se si desidera importare qualsiasi modulo dalla directory principale. Poi,

from ..module_name import *

Inoltre, se si desidera importare qualsiasi modulo dalla directory principale. Poi,

from ...module_name.another_module import *

In questo modo è possibile importare qualsiasi metodo particolare se lo si desidera.


6
Questo sembra essere come dovrebbe essere, ma per qualche ragione questo non funziona allo stesso modo degli sys.path.appendhack sopra e non posso importare la mia lib in questo modo. Questo è quello che ho provato a fare prima di iniziare a google :) Ok, è stato questoValueError: attempted relative import beyond top-level package
juzzlin,

10

Per me il più breve e il mio oneliner preferito per accedere alla directory principale è:

sys.path.append(os.path.dirname(os.getcwd()))

o:

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd () restituisce il nome della directory di lavoro corrente, os.path.dirname (directory_name) restituisce il nome della directory per quella passata.

In realtà, a mio avviso, l'architettura del progetto Python dovrebbe essere eseguita nel modo in cui nessun modulo dalla directory figlio utilizzerà alcun modulo dalla directory padre. Se succede qualcosa del genere, vale la pena ripensare all'albero del progetto.

Un altro modo è aggiungere la directory principale alla variabile di ambiente di sistema PYTHONPATH.


2
+1 per menzionare la possibilità di dover ristrutturare il progetto (invece di lanciare un problema al problema)
semore_1267

questo non ottiene il genitore, ottiene la corrente
Ricky Levi,

9

In un quaderno Jupyter

Finché lavori in un notebook Jupyter, questa breve soluzione potrebbe essere utile:

%cd ..
import nib

Funziona anche senza __init__.py file.

L'ho provato con Anaconda3 su Linux e Windows 7.


2
Non avrebbe senso aggiungere %cd -dopo l'importazione, quindi la directory corrente del notebook rimane invariata.
Dror


5

Quando non ci si trova in un ambiente pacchetto con __init__.pyfile, la libreria pathlib (inclusa con> = Python 3.4) rende molto conciso e intuitivo aggiungere il percorso della directory padre al PYTHONPATH:

import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))

è possibile utilizzare init .py per aggirare questo problema?
ArtificiallyIntelligence,

4

stesso tipo di stile della risposta passata, ma in meno righe: P

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

il file restituisce la posizione in cui si sta lavorando


Per me il file è il nome file senza il percorso incluso. Lo eseguo usando "ipy nomefile.py".
Curtis Yallop,

Ciao @CurtisYallop nell'esempio sopra stiamo aggiungendo la dir che contiene il file [in cui ci troviamo attualmente] in cui si trova il file python. La chiamata os.path.dirname con il nome del file dovrebbe restituire il percorso del file e noi stanno aggiungendo CHE al percorso e non esplicitamente al file - HTH :-)
YFP

Supponi che __file__ contenga sempre il percorso più il file. A volte contiene solo il nome file senza il percorso.
Curtis Yallop,

@CurtisYallop - niente affatto, suppongo che il file sia il nome del file e sto usando os.path.dirname () per ottenere il percorso del file. Hai un esempio di questo non funziona? Gradirei i passaggi per riprodurre
YFP

1
@CurtisYallop - No, non lo sono! Sto dicendo che: print / _ / file / _ / ti darà il nome del file nel tuo esempio sopra "file.py" - Sto quindi dicendo che puoi usare os.path.dirname () per estrarre il percorso completo da quello . Se stai chiamando da una strana posizione e desideri quella relazione, allora puoi facilmente trovare la tua directory di lavoro attraverso il modulo os - Jeez!
YFP

1

Lavora con le biblioteche. Crea una libreria chiamata pennino, installala usando setup.py, lascia che risieda nei pacchetti del sito e i tuoi problemi sono risolti. Non devi riempire tutto ciò che fai in un unico pacchetto. Spezzalo in pezzi.


1
La tua idea è buona, ma alcune persone potrebbero voler raggruppare il loro modulo con il loro codice - forse per renderlo portatile e non richiedere di inserire i loro moduli site-packagesogni volta che lo eseguono su un altro computer.
Edward,

0

In un sistema Linux, è possibile creare un collegamento software dalla cartella "life" al file nib.py. Quindi, puoi semplicemente importarlo come:

import nib
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.