2009-10-12 22 views
60

Diciamo che ho la seguente struttura di directory:circolare dipendenza dalle importazioni in Python

a\ 
    __init__.py 
    b\ 
     __init__.py 
     c\ 
      __init__.py 
      c_file.py 
     d\ 
      __init__.py 
      d_file.py 

Nel pacchetto di __init__.pya, il pacchetto c viene importato. Ma c_file.py importa a.b.d.

il programma non riesce, dicendo b non esiste quando c_file.py cerca di importare a.b.d. (E in realtà non esiste, perché eravamo nel bel mezzo dell'importazione.)

Come si può porre rimedio a questo problema?

+1

Forse potresti provare le importazioni relative? http://stackoverflow.com/questions/72852/how-to-do-relative-imports-in-python – eremzeit

+1

questo può aiutare https://ncoghlan_devs-python-notes.readthedocs.org/en /latest/python_concepts/import_traps.html – maazza

+0

anche come riferimento, sembra che le importazioni circolari siano consentite su python 3.5 (e probabilmente oltre) ma non su 3.4 (e probabilmente sotto). –

risposta

53

Se a dipende da c, c dipende da a, in realtà non sono la stessa unità?

Si dovrebbe davvero esaminare il motivo per cui si sono suddivisi a e c in due pacchetti, perché o si dispone di un codice da dividere in un altro pacchetto (per far sì che entrambi dipendano dal nuovo pacchetto, ma non l'uno dall'altro), oppure dovresti unirli in un unico pacchetto.

+83

Sì, potrebbero essere considerati lo stesso pacchetto. Ma se questo si traduce in un file enormemente enorme, allora non è pratico. Sono d'accordo che frequentemente, le dipendenze circolari significano che il design dovrebbe essere ripensato. Ma ci sono alcuni modelli di design in cui è appropriato (e dove la fusione dei file risulterebbe in un file enorme) quindi penso che sia dogmatico dire che i pacchetti dovrebbero essere combinati o il design dovrebbe essere rivalutato. –

140

Si può rinviare l'importazione, per esempio in a/__init__.py:

def my_function(): 
    from a.b.c import Blah 
    return Blah() 

che è, rinviare l'importazione fino a quando non è realmente necessario. Tuttavia, vorrei anche dare un'occhiata alle mie definizioni/usi del pacchetto, poiché una dipendenza ciclica come quella indicata potrebbe indicare un problema di progettazione.

+5

Contento che tu l'abbia fatto notare, questo ha funzionato perfettamente per me. Ho preso attentamente in considerazione il mio progetto e ho pensato che in questo caso, è buono. –

+3

A volte i riferimenti circolari sono veramente inevitabili. Questo è l'unico approccio che funziona per me in queste circostanze. –

+1

Non aggiungerebbe un sovraccarico in ogni chiamata di pippo? –

-3

Un'altra soluzione è utilizzare un proxy per il file d_.

Ad esempio, supponiamo di voler condividere la classe blah con il file_file. Il d_file contiene quindi:

class blah: 
    def __init__(self): 
     print("blah") 

Ecco cosa si entra in c_file.py:

# do not import the d_file ! 
# instead, use a place holder for the proxy of d_file 
# it will be set by a's __init__.py after imports are done 
d_file = None 

def c_blah(): # a function that calls d_file's blah 
    d_file.blah() 

E in una è init .py:

from b.c import c_file 
from b.d import d_file 

class Proxy(object): # module proxy 
    pass 
d_file_proxy = Proxy() 
# now you need to explicitly list the class(es) exposed by d_file 
d_file_proxy.blah = d_file.blah 
# finally, share the proxy with c_file 
c_file.d_file = d_file_proxy 

# c_file is now able to call d_file.blah 
c_file.c_blah() 
+11

modificare gli attributi globali del modulo in un file diverso come quello che porterà rapidamente a un incubo – Antimony

19

mi sono chiesto questo un paio di volte (di solito mentre si tratta di modelli che devono conoscersi l'un l'altro). La soluzione semplice è solo per importare l'intero modulo, quindi fare riferimento alla cosa di cui hai bisogno.

Così, invece di fare

from models import Student 

in uno, e

from models import Classroom 

nell'altra, basta fare

import models 

in uno di essi, quindi chiamare models.Classroom quando ne hai bisogno.

+1

Elegante e geniale !! – nish

0

Il problema è che quando si esegue da una directory, per impostazione predefinita solo i pacchetti che sono sottodirectory sono visibili come importazioni candidate, quindi non è possibile importare a.b.d. Puoi comunque importare b.d. poiché b è un pacchetto secondario di a.

Se si desidera importare a.b.d in c/__init__.py, è possibile eseguire questa operazione modificando il percorso del sistema in modo che sia una directory sopra a e modificare l'importazione in a/__init__.py da importare a.b.c.

tuo a/__init__.py dovrebbe essere simile a questo:

import sys 
import os 
# set sytem path to be directory above so that a can be a 
# package namespace 
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) 
sys.path.insert(0,DIRECTORY_SCRIPT+"/..") 
import a.b.c 

Un'ulteriore difficoltà sorge quando si desidera eseguire moduli in C come script. Qui i pacchetti aeb non esistono. È possibile modificare il numero __int__.py nella directory c per puntare sys.path alla directory di livello superiore e quindi importare __init__ in qualsiasi modulo in c per poter utilizzare il percorso completo per importare a.b.d. Dubito che sia una buona pratica importare __init__.py ma ha funzionato per i miei casi d'uso.