Immagina questa struttura di directory:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
Sto codificando mod1
, e ho bisogno di importare qualcosa da mod2
. Come dovrei farlo?
Ho provato from ..sub2 import mod2
ma sto ottenendo un "Tentativo di importazione relativa in non-pacchetto".
Ho cercato su Google, ma ho trovato solo hack "sys.path
manipulation". Non c'è un modo pulito?
Modifica: tutti i miei __init__.py
'sono attualmente vuoti
Edit2: Sto cercando di farlo perché sub2 contiene classi che sono condivise tra sotto pacchetti (sub1
, subX
, ecc.).
Edit3: Il comportamento che sto cercando è lo stesso descritto in PEP 366 (grazie John B)
Tutti sembrano voler dirti cosa dovresti fare piuttosto che rispondere alla domanda.
Il problema è che stai eseguendo il modulo come '__main__' passando il mod1.py come argomento all'interprete.
Da PEP 328 :
Le importazioni relative utilizzano l'attributo __di un modulo per determinare la posizione del modulo nella gerarchia dei pacchetti. Se il nome del modulo non contiene alcuna informazione sul pacchetto (ad esempio è impostato su "__main__"), le importazioni relative vengono risolte come se il modulo fosse un modulo di livello superiore, indipendentemente da dove il modulo si trovi effettivamente sul file system.
In Python 2.6, stanno aggiungendo la possibilità di fare riferimento ai moduli relativi al modulo principale. PEP 366 descrive la modifica.
Aggiornamento : Secondo Nick Coghlan, l'alternativa consigliata è eseguire il modulo all'interno del pacchetto usando l'opzione -m.
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
python main.py
.main.py
fa: import app.package_a.module_a
module_a.py
fa import app.package_b.module_b
In alternativa 2 o 3 potrebbero usare: from app.package_a import module_a
Funzionerà finché hai app
nel tuo PYTHONPATH. main.py
potrebbe essere ovunque quindi.
Quindi scrivi un setup.py
per copiare (installare) l'intero pacchetto app e i pacchetti secondari nelle cartelle python del sistema di destinazione e main.py
nelle cartelle degli script del sistema di destinazione.
Ecco la soluzione che funziona per me:
Faccio le importazioni relative come from ..sub2 import mod2
e poi, se voglio eseguire mod1.py
, vado nella directory padre di app
ed eseguo il modulo usando python -m switch come python -m app.sub1.mod1
.
La vera ragione per cui questo problema si verifica con le importazioni relative, è che le importazioni relative funzionano prendendo la proprietà __name__
del modulo. Se il modulo viene eseguito direttamente, allora __name__
è impostato su __main__
e non contiene alcuna informazione sulla struttura del pacchetto. E questo è il motivo per cui Python si lamenta dell'errore relative import in non-package
.
Quindi, usando l'opzione -m fornite le informazioni sulla struttura del pacchetto a python, attraverso cui è possibile risolvere correttamente le importazioni relative.
Ho riscontrato questo problema molte volte mentre facevo importazioni relative. E, dopo aver letto tutte le risposte precedenti, non ero ancora in grado di capire come risolverlo, in modo pulito, senza la necessità di inserire il codice boilerplate in tutti i file. (Anche se alcuni dei commenti sono stati davvero utili, grazie a @ncoghlan e @XiongChiamiov)
Spero che questo aiuti qualcuno che sta combattendo con il problema delle importazioni relative, perché passare attraverso PEP non è davvero divertente.
"Guido visualizza gli script in esecuzione all'interno di un pacchetto come anti-pattern" (respinto PEP-3122 )
Ho passato così tanto tempo a cercare una soluzione, a leggere i post correlati qui su Stack Overflow e a dirmi "ci deve essere un modo migliore!". Sembra che non ci sia.
Questo è risolto al 100%:
Importa impostazioni/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')
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
Sto usando questo snippet per importare moduli dai percorsi, spero che sia d'aiuto
spiegazione di nosklo's
risposta con esempi
nota: tutti i file __init__.py
sono vuoti.
main.py
app/ ->
__init__.py
package_a/ ->
__init__.py
fun_a.py
package_b/ ->
__init__.py
fun_b.py
def print_a():
print 'This is a function in dir package_a'
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()
from app.package_b import fun_b
fun_b.print_b()
se esegui $ python main.py
restituisce:
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
from app.package_b import fun_b
from app.package_a.fun_a import print_a
quindi file nella cartella package_b
file utilizzato nella cartella package_a
, che è quello che vuoi. Destra??
Questo è sfortunatamente un hacking sys.path, ma funziona abbastanza bene.
Ho riscontrato questo problema con un altro livello: avevo già un modulo con il nome specificato, ma era il modulo sbagliato.
quello che volevo fare era il seguente (il modulo dal quale stavo lavorando era module3):
mymodule\
__init__.py
mymodule1\
__init__.py
mymodule1_1
mymodule2\
__init__.py
mymodule2_1
import mymodule.mymodule1.mymodule1_1
Nota che ho già installato mymodule, ma nella mia installazione non ho "mymodule1"
e vorrei ottenere un ImportError perché stava cercando di importare dai miei moduli installati.
Ho provato a fare un sys.path.append, e non ha funzionato. Che cosa ha funzionato è stato un sys.path.insert
if __== '__main__':
sys.path.insert(0, '../..')
Così gentile, ma ha funzionato tutto! Quindi tieni presente che se vuoi che la tua decisione sovrascriva altri percorsi , devi usare sys.path.insert (0, pathname) per ottenerlo lavorare! Questo è stato un punto molto frustrante per me, molti dicono di usare la funzione "aggiungi" a sys.path, ma questo non funziona se hai già un modulo definito (lo trovo molto strano)
Lasciatemi solo mettere questo qui per il mio riferimento. So che non è un buon codice Python, ma avevo bisogno di uno script per un progetto su cui stavo lavorando e volevo mettere lo script in una directory scripts
.
import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Come dice @EvgeniSergeev nei commenti all'OP, puoi importare il codice da un file .py
in una posizione arbitraria con:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Questo è preso da questo SO answer .
Dai un'occhiata a http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Potresti farlo
from .mod1 import stuff
Da Python doc ,
In Python 2.5, è possibile cambiare il comportamento dell'importazione in importazioni assolute utilizzando una direttiva
from __future__ import absolute_import
. Questo comportamento di importazione assoluta diventerà il valore predefinito in una versione futura (probabilmente Python 2.7). Una volta che le importazioni assolute sono l'impostazione predefinita,import string
troverà sempre la versione della libreria standard. Si consiglia agli utenti di iniziare a utilizzare le importazioni assolute il più possibile, quindi è preferibile iniziare a scriverefrom pkg import string
nel codice
Oltre a ciò che ha detto John B, sembra che l'impostazione della variabile __package__
dovrebbe aiutare, invece di cambiare __main__
che potrebbe rovinare altre cose. Ma per quanto ho potuto testare, non funziona come dovrebbe.
Ho lo stesso problema e né PEP 328 né 366 risolvo completamente il problema, poiché entrambi, entro la fine della giornata, hanno bisogno che il capo del pacchetto sia incluso in sys.path
, per quanto ho potuto capire.
Vorrei anche ricordare che non ho trovato come formattare la stringa che dovrebbe andare in quelle variabili. È "package_head.subfolder.module_name"
o cosa?
Ho scoperto che è più facile impostare la variabile di ambiente "PYTHONPATH" nella cartella principale:
bash$ export PYTHONPATH=/PATH/TO/APP
poi:
import sub1.func1
#...more import
naturalmente, PYTHONPATH è "globale", ma non mi ha ancora creato problemi.