2015-10-31 19 views
5

Ho la seguente struttura del pacchettoPython importare uno subpackage senza altri

package 
    __init__.py 
    sub1 
     __init__.py 
     foo.py  # Contains class Foo 
    sub2 
     __init__.py 
     bar.py  # Contains class Bar 

voglio essere in grado di appena import package e hanno package.Foo e package.Bar, cioè voglio avere i sottopacchetti essere trasparente per gli utenti.

Il problema è che l'importazione di sub2 richiede molto tempo e molti utenti non si preoccupano affatto delle cose in sub2 e vogliono solo le cose in sub1. Quindi voglio che gli utenti possano dire import package.sub1 o from package import sub1 a solo import sub1 e saltare l'importazione di sub2.

so di poter ottenere la prima parte avendo package/__init__.py contengono

from .sub1 import * 
from .sub2 import * 

e avendo package/sub1/__init__.py essere from .foo import Foo e allo stesso modo per sub2. Tuttavia, questo importa sempre sub1 e sub2 anche se l'utente tenta di importare solo package.sub1.

Corrispondentemente, posso ottenere la seconda parte avendo package/__init__.py vuoto e utilizzando lo stesso sub1/__init__.py come sopra. Tuttavia, il solo dire import package non carica sub1 o sub2, quindi gli utenti dovrebbero caricarli esplicitamente e quindi fare riferimento a package.sub1.Foo.

Idealmente una soluzione funzionerebbe sia in 2.7.10 che in 3.5.0, ma accetterò l'una o l'altra se entrambe non sono possibili.

+1

una domanda migliore è perché hai un codice di lunga durata a livello di modulo in 'sub2' – jfs

+0

Quindi, in realtà, la tua domanda è : "Voglio' import package.sub1' per fare meno cose di un semplice 'pacchetto di importazione ', giusto? Assente qualche strano trucco come leggere il bytecode in anticipo, penso che non sia possibile.' Import AB' deve importare A' prima nota nella parte inferiore di https: // docs.python.org/3.6/library/importlib.html (ultimo blocco di codice, riga 15), 'import_module' è una funzione ricorsiva. Tuttavia, si può facilmente avere un terzo sottopackage, 'package.everything', che importerà realmente' sub1' e 'sub2' nello stesso spazio dei nomi. – Veky

+0

@Veky: Questo è esattamente ciò che ho fatto (beh, 'package.all', ma abbastanza vicino) – Alec

risposta

0

si potrebbe aggiungere i collegamenti alla __init__.py del modulo:

pacchetto/__ init__.py

__all__ = [ 
    ... add everything you want to be listed for this module 
    'Foo', 
    'Bar', 
    ... 
] 
from package.sub1.foo import Foo 
from package.sub2.bar import Bar 

si dovrebbe ora essere in grado di chiamare:

from package import Bar 
+0

abilita 'package.Bar' dopo' import package' ma non gestisce la parte "lazy import" della domanda. – jfs

4

Il La classe LazyLoader viene fornita esattamente per questo tipo di situazione: posticipare il caricamento del modulo quando è effettivamente usato, invece di importarlo.

Costruire un caricatore pigri si può seguire l'esempio nella documentazione:

suffixes = importlib.machinery.SOURCE_SUFFIXES 
loader = importlib.machinery.SourceFileLoader 
lazy_loader = importlib.util.LazyLoader.factory(loader) 
finder = importlib.machinery.FileFinder(path, [(lazy_loader, suffixes)]) 

quindi è possibile utilizzare finder.find_spec per ottenere le specifiche di un modulo e passare il risultato a Loader.create_module per caricarlo.

Questo è un po 'ingombrante da fare manualmente per un solo modulo.

Si noti che alla ricerca di "python import pigro" sono disponibili diverse soluzioni con pro e contro diversi, alcuni dei quali vengono eseguiti in python2.x. Tuttavia la classe LazyLoader sopra è il modo ufficiale di farlo in python3.5 +

+0

Non riesco a ottenere questo per caricare effettivamente il materiale dal subpackage. Se 'sub2/__ init __. Py' ha' dalla barra di importazione .bar, restituisce 'ImportError: Nessun modulo denominato 'package.bar'' Se usa un'importazione assoluta, ottengo' ValueError: module object for 'package.sub2' sostituito in sys.modules durante un carico pigro' Sembra anche che questo avrà solo i simboli sotto sub2, quindi dovrei ancora dire 'pacchetto.sub2.Bar' piuttosto che solo' pacchetto .Bar' – Alec

+0

Per chi guarda questo in futuro, se tu (come me) non riesci a far funzionare il caricatore pigro, la mia soluzione era quella di avere un pacchetto 'package.all' (o, comunque, se preferisci) che importa tutto e il pacchetto di livello superiore non importa nulla. Vedi i commenti sulla domanda per ulteriori discussioni – Alec