2012-01-02 6 views
15

Nella gerarchia sottostante, esiste un modo comodo e universale per fare riferimento al pacchetto top_pagina usando un termine generico in tutto il file .py sotto? Mi piacerebbe avere un modo coerente per importare altri moduli, in modo che anche quando il "top_package" cambia nome, non si rompe nulla.Come fare riferimento al modulo di primo livello in Python all'interno di un pacchetto?

Non sono favorevole all'uso dell'importazione relativa come "..level_one_a" in quanto il percorso relativo sarà diverso da ciascun file python di seguito. Sto cercando un modo:

  1. Ogni file python può avere la stessa istruzione di importazione per lo stesso modulo nel pacchetto.
  2. Un riferimento di disaccoppiamento a "top_package" in qualsiasi file .py all'interno del pacchetto, quindi qualsiasi nome "top_package" cambierà, non si interrompe nulla.

    top_package/ 
        __init__.py 
        level_one_a/ 
        __init__.py 
        my_lib.py 
        level_two/ 
         __init__.py 
         hello_world.py 
        level_one_b/ 
        __init__.py 
        my_lib.py 
        main.py 
    
+1

Perché vuoi un riferimento al modulo di livello superiore? (A volte questa risposta rivela il vero problema/soluzione.) –

+1

Voglio rendere il pacchetto il più possibile riutilizzabile, in modo che quando rinominare il modulo principale, tutto funzioni ancora normalmente senza dover cambiare ciascuno il nome di importazione all'interno. – hllau

risposta

10

Questo dovrebbe fare il lavoro:

top_package = __import__(__name__.split('.')[0]) 

Il trucco è che per ogni modulo la variabile __name__ contiene il percorso completo del modulo separati da punti come, ad esempio, top_package.level_one_a.my_lib. Quindi, se si desidera ottenere il nome del pacchetto principale, è sufficiente ottenere il primo componente del percorso e importarlo utilizzando __import__.

Nonostante il nome della variabile utilizzato per accedere al pacchetto sia ancora chiamato top_package, è possibile rinominare il pacchetto e se funzionerà ancora.

+1

-1 La domanda ha richiesto una soluzione che non dipendesse dal nome "top_package". –

+0

@ Jon-Eric Grazie per il tuo commento. Ho risolto la mia risposta per funzionare indipendentemente dal nome del pacchetto principale. – jcollado

+1

Questo non è completamente corretto; se il modulo corrente non viene importato dal livello superiore, il suo '__name__' non conterrà il percorso completo di questo modulo, ma solo parte di ciò, che partirà da dove si importa questo modulo. – thuzhf

0

Credo che # 2 sia impossibile senza utilizzare le importazioni relative o il pacchetto con nome. Devi specificare quale modulo importare chiamando esplicitamente il suo nome o usando un'importazione relativa. altrimenti come potrebbe l'interprete sapere cosa vuoi?

Se rendere l'applicazione launcher un livello sopra top_level/ e lo hanno import top_leve L È possibile quindi fare riferimento top_level.* da qualsiasi punto all'interno della confezione top_level.

(posso mostrarvi un esempio dal software su cui sto lavorando: http://github.com/toddself/beerlog/)

1

Si potrebbe utilizzare una combinazione della funzione __import__() e l'attributo __path__ di un pacchetto.

Ad esempio, si supponga di voler importare <whatever>.level_one_a.level_two.hello_world da qualche altra parte nel pacchetto. Si potrebbe fare qualcosa di simile:

import os 
_temp = __import__(__path__[0].split(os.sep)[0] + ".level_one_a.level_two.hello_world") 
my_hello_world = _temp.level_one_a.level_two.hello_world 

Questo codice è indipendente dal nome del pacchetto di livello superiore e può essere utilizzato ovunque nel pacchetto. È anche piuttosto brutto.

5

Metti il ​​pacchetto e lo script main in una directory contenitore esterno, in questo modo:

container/ 
    main.py 
    top_package/ 
     __init__.py 
     level_one_a/ 
      __init__.py 
      my_lib.py 
      level_two/ 
       __init__.py 
       hello_world.py 
     level_one_b/ 
      __init__.py 
      my_lib.py 

Quando main.py viene eseguito, la sua directory padre (container) saranno aggiunti automaticamente alla partenza di sys.path. E poiché top_package è ora nella stessa directory, può essere importato da qualsiasi punto all'interno dell'albero dei pacchetti.

Così hello_world.py potrebbe importare level_one_b/my_lib.py come questo:

from top_package.level_one_b import my_lib 

Non importa quale sia il nome della directory contenitore è, o dove si trova, le importazioni saranno sempre lavorare con questa disposizione.

Ma si noti che, nell'esempio originale, top_package potrebbe facilmente funzionare come directory del contenitore stesso. Tutto quello che dovresti fare è rimuovere top_package/__init__.py, e ti ritroverai con la stessa disposizione.

L'istruzione import precedente sarebbe poi cambiare:

from level_one_b import my_lib 

e si sarebbe libero di rinominare top_package tuttavia si voleva.

+0

Se si rimuove top_package/__ init__.py, quindi level_one_a e level_one_b non saranno in grado di importare l'uno dall'altro. – DonGar

+0

@DonGar. No, funzionerà perfettamente. Ma nota che mi riferivo alla struttura nella domanda dei PO, non a quella mostrata nella mia risposta. La differenza fondamentale è la posizione dello script 'main.py'. – ekhumoro

+0

La parte principale della tua risposta sono d'accordo, ma il paragrafo che inizia con "Ma nota che" è quello con cui non sono d'accordo. A meno che non intendiate anche spostare sia level_one_a che level_one_b in un nuovo sottomodulo di top_container. – DonGar

0

Questo funziona dall'interno di un modulo di libreria:

import __main__ as main_package 
TOP_PACKAGE = main_package.__package__.split('.')[0]