2015-10-31 6 views
5

Ich habe Struktur das folgende PaketPython importieren eine subpackage ohne andere

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

Ich möchte in der Lage, nur import package und package.Foo und package.Bar haben, das heißt ich die Subpackages für die Nutzer transparent sein haben wollen.

Der Catch ist, dass der Import von Sub2 eine lange Zeit dauert, und viele Benutzer kümmern sich überhaupt nicht um die Sachen in Sub2 und wollen nur die Sachen in Sub1. So möchte ich Benutzer in der Lage sein zu sagen import package.sub1 oder from package import sub1 zu nur importieren Sie Sub1 und überspringen Sie den Import von Sub2.

Ich weiß, dass ich den ersten Teil package/__init__.py, indem er erreichen kann enthalten

from .sub1 import * 
from .sub2 import * 

und package/sub1/__init__.pyfrom .foo import Foo sein mit und in ähnlicher Weise für sub2. Dies führt jedoch dazu, dass Sub1 und Sub2 immer importiert werden, auch wenn der Benutzer nur versucht, package.sub1 zu importieren.

Entsprechend kann ich den zweiten Teil erreichen, indem package/__init__.py leer sein und denselben sub1/__init__.py wie oben verwenden. Wenn Sie jedoch nur import package sagen, wird Sub1 oder Sub2 nicht geladen, sodass Benutzer sie explizit laden müssen und dann auf package.sub1.Foo verweisen müssen.

Im Idealfall würde eine Lösung sowohl in 2.7.10 und 3.5.0 funktionieren, aber ich werde das eine oder das andere akzeptieren, wenn beides nicht möglich ist.

+1

eine bessere Frage zu machen, warum haben Sie einen lang laufenden Code auf der Modulebene in 'sub2' – jfs

+0

Also, in der Tat ist Ihre Frage : "Ich möchte' import package1 'weniger tun, als '' import package'', oder? Es fehlt ein seltsamer hack wie das Lesen des Bytecodes im Voraus, ich denke, das ist nicht möglich. 'Import AB' muss 'Import A' Hinweis: Am Ende von https: // docs.python.org/3.6/library/importlib.html (letzter Codeblock, Zeile 15), 'import_module' ist eine rekursive Funktion. Sie können jedoch leicht ein drittes Unterpaket "package.everything" erstellen, das "sub1" und "sub2" in denselben Namespace importiert. – Veky

+0

@Veky: Das ist tatsächlich genau das, was ich getan habe (naja, 'package.all', aber nahe genug) – Alec

Antwort

0

könnten Sie Ihre Verknüpfungen zum __init__.py Ihres Moduls hinzu:

Paket/__ 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 

jetzt sollten Sie in der Lage zu nennen:

from package import Bar 
+0

dies aktiviert 'package.Bar' nach' import package', aber es behandelt nicht den "lazy import" -Teil der Frage. – jfs

4

Die LazyLoader Klasse wird für genau diese Art von Situation zur Verfügung gestellt: Verschiebung des Ladens des Moduls, wenn es tatsächlich ist verwendet, anstatt zum Zeitpunkt des Importierens.

Um einen faulen Loader bauen Sie das Beispiel in der Dokumentation folgen:

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

dann können Sie finder.find_spec verwenden, um die Spezifikation eines Moduls zu erhalten und das Ergebnis an Loader.create_module passieren sie zu laden.

Dies ist ein wenig mühselig für nur ein Modul manuell zu tun.

Beachten Sie, dass bei der Suche nach "Lazy Import Python" finden Sie eine ganze Reihe von Lösungen, die verschiedene Vor- und Nachteile haben, von denen einige in python2.x laufen. Aber die LazyLoader Klasse oben ist der offizielle Weg, es in python3.5 +

+0

Ich kann das nicht bekommen, um das Zeug wirklich aus dem Unterpaket zu laden. Wenn 'sub2/__ init __. Py'' aus .bar import Bar' hat, gibt es 'ImportError: Kein Modul mit dem Namen 'package.bar'' Wenn es einen absoluten Import verwendet, bekomme ich' ValueError: module object for 'package2' wird in sys.modules während einer Lazy Load ersetzt ' Es scheint auch so, als würde dies nur die Symbole unter Sub2 haben, also würde ich immer 'package2.Bar' anstatt nur' package sagen müssen .Bar' – Alec

+0

Für alle, die sich in Zukunft damit beschäftigen, wenn Sie (wie ich) keinen faulen Loader zur Arbeit bekommen können, war meine Lösung, einfach ein 'package.all' (oder .alles, wenn Sie bevorzugen) zu importieren alles, und das Top-Level-Paket importiert nichts. Weitere Informationen finden Sie in den Kommentaren zu dieser Frage – Alec

Verwandte Themen