it-swarm.it

Come fare le importazioni relative in Python?

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)

487
Joril

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.

306
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Esegui python main.py.
  2. main.py fa: import app.package_a.module_a
  3. 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.

116
nosklo

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.

114
Pankaj

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

46
lesnik

Questo è risolto al 100%:

  • app /
    • main.py
  • impostazioni/
    • local_setings.py

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

24
iElectric

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

app/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app/package_b/fun_b.py

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

main.py

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
  • main.py fa: from app.package_b import fun_b
  • fun_b.py fa 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??

21
suhailvs

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)

11
Garrett Berg

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__), "..")))
10
milkypostman

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 .

8
LondonRob

Dai un'occhiata a http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Potresti farlo

from .mod1 import stuff
4
mossplix

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 scrivere from pkg import string nel codice

2
jung rhew

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?

1
Gabriel

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.

1
Andrew_1510