2009-09-22 15 views
7

In una grande applicazione che sto lavorando, diverse persone importano gli stessi moduli in modo diverso ad es. importazione x o da importazione x gli effetti collaterali y del cioè x è importata due volte e può introdurre bug molto sottile, se qualcuno è fare affidamento su attributi globalimodulo reimportato se importato da un percorso diverso

esempio supponiamo Ho un mypakcage pacchetto con tre file di mymodule.py, main.py e init .py

contenuti mymodule.py

l = [] 
class A(object): pass 

contenuti main.py

def add(x): 
    from mypackage import mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    import mymodule 
    return mymodule.l 

add(1) 
print "lets check",get() 

add(1) 
print "lets check again",get() 

stampa

updated list [1] 
lets check [] 
updated list [1, 1] 
lets check again [] 

perché ora ci sono due liste in due moduli diversi, allo stesso modo la classe A è diversa Per me sembra abbastanza seria perché le classi stesse saranno trattate in modo diverso ad es. sotto stampe di codici falsi

def create(): 
    from mypackage import mymodule 
    return mymodule.A() 

def check(a): 
    import mymodule 
    return isinstance(a, mymodule.A) 

print check(create()) 

Domanda:

C'è un modo per evitare questo? tranne che impone che il modulo debba essere importato one-way. Non può essere gestito dal meccanismo di importazione di Python, ho visto diversi bug relativi a questo nel codice django e anche altrove.

risposta

3

Posso solo replicarlo se main.py è il file che stai effettivamente eseguendo. In tal caso si otterrà la directory corrente di main.py sul percorso sys. Ma apparentemente hai anche un percorso di sistema impostato in modo che il mypackage possa essere importato.

In questa situazione Python non si renderà conto che mymodule e mypackage.mymodule sono lo stesso modulo e ottieni questo effetto. Questo cambiamento illustra questo:

def add(x): 
    from mypackage import mymodule 
    print "mypackage.mymodule path", mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    import mymodule 
    print "mymodule path", mymodule 
    return mymodule.l 

add(1) 
print "lets check",get() 

add(1) 
print "lets check again",get() 


$ export PYTHONPATH=. 
$ python mypackage/main.py 

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'> 

Ma aggiungere un altro mainfile, nella directory currect:

realmain.py: 
from mypackage import main 

e il risultato è diverso:

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 

così ho il sospetto che avete la vostra principale file python all'interno del pacchetto. E in tal caso la soluzione è non farlo. :-)

+0

hmmm in realtà che non è il caso, vedrò come replicare con main.py pacchetto esterno –

+0

non hai ragione :) –

3

Ogni spazio dei nomi modulo viene importato una sola volta. Il problema è che li stai importando in modo diverso. Sul primo si sta importando dal pacchetto globale e nel secondo si sta eseguendo un locale, non confezionato import. Python vede i moduli come diversi. La prima importazione è internamente memorizzata nella cache come mypackage.mymodule e la seconda solo come mymodule.

Un modo per risolvere questo è utilizzare sempre importazioni assolute.Cioè, sempre dare i percorsi di importazione assoluti modulo dal pacchetto di livello superiore in poi:

def add(x): 
    from mypackage import mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    from mypackage import mymodule 
    return mymodule.l 

Ricordate che il vostro punto di ingresso (il file si esegue, main.py) anche al di fuori dovrebbe essere il pacchetto. Quando si desidera che il codice del punto di ingresso si trovi all'interno del pacchetto, in genere si utilizza invece un piccolo script. Esempio:

runme.py, al di fuori del pacchetto:

from mypackage.main import main 
main() 

E in main.py si aggiunge:

def main(): 
    # your code 

trovo this document da Jp Calderone di essere una grande punta su come (non) la struttura il tuo progetto Python. Seguendolo non avrai problemi. Presta attenzione alla cartella bin - è al di fuori del pacchetto. Io riprodurre l'intero testo qui:

Struttura del filesystem di un progetto Python

fare:

  • nome della directory qualcosa relative al vostro progetto. Ad esempio, se il progetto è denominato "Twisted", , denominare la directory di livello superiore per i relativi file di origine Twisted. Quando si eseguono le versioni , è necessario includere un suffisso numerico della versione : Twisted-2.5.
  • crea una directory Twisted/bin e metti i tuoi eseguibili lì, se hai ne hai. Non dare loro un'estensione , anche se sono file di origine Python . Non inserire alcun codice in ad eccezione dell'importazione e chiamare la funzione principale definita altrove nei progetti.
  • Se il progetto è esprimibile come un singolo file sorgente Python , poi metterlo nella directory e denominarlo qualcosa relative al vostro progetto. Ad esempio, Twisted/twisted.py. Se avete bisogno di più file di origine, creare un pacchetto invece (Twisted/twisted/, con una Twisted/twisted/__init__.py vuoto) e luogo i file di origine in esso. Per esempio , Twisted/twisted/internet.py.
  • mettere il test di unità in un sub-pacchetto di vostro pacchetto (nota - questo significa che l'opzione singolo file sorgente Python sopra era un trucco - è sempre necessario ad almeno un altro file per l'unità test). Ad esempio, Twisted/twisted/test/. Ovviamente, ne fanno un pacchetto con Twisted/twisted/test/__init__.py. Inserire test in file come Twisted/twisted/test/test_internet.py.
  • aggiungi Twisted/README e T wisted/setup.py per spiegare e installa il software, rispettivamente, se ti senti bene.

non Do:

  • mettere la vostra fonte in una directory denominata src o lib. Questo lo rende difficile da eseguire senza l'installazione.
  • inserire i test al di fuori del pacchetto Python . Ciò rende difficile eseguire i test rispetto a una versione installata.
  • creare un pacchetto con solo __init__.py e quindi inserire tutto il codice in __init__.py. Basta creare un modulo invece di un pacchetto, è il più semplice.
  • cercare di venire con hack magici per far sì che Python in grado di importare il modulo o il pacchetto senza dover all'utente di aggiungere la directory contenente al loro percorso di importazione (sia via PYTHONPATH o qualche altro meccanismo di ). Non gestirai correttamente tutti i casi e gli utenti riceveranno arrabbiato quando il tuo software non funziona nel loro ambiente.
Problemi correlati