it-swarm.com.de

Wie importiere ich alle Submodule?

Ich habe eine Verzeichnisstruktur wie folgt:

| main.py
| scripts
|--| __init__.py
   | script1.py
   | script2.py
   | script3.py

Aus main.py wird das Modul scripts importiert. Ich habe versucht, pkgutils.walk_packages in Kombination mit __all__ zu verwenden, aber damit kann ich nur alle Submodule direkt unter main mit from scripts import * importieren. Ich möchte sie alle unter scripts bekommen. Was wäre der sauberste Weg, um alle Submodule von scripts zu importieren, damit ich von main aus auf scripts.script1 zugreifen kann?

EDIT: Es tut mir leid, dass ich ein bisschen vage war. Ich möchte die Submodule zur Laufzeit importieren, ohne sie explizit in __init__.py anzugeben. Ich kann pkgutils.walk_packages verwenden, um die Namen der Submodule abzurufen (es sei denn, jemand kennt einen besseren Weg), bin mir jedoch nicht sicher, wie diese Namen (oder die von walk_packages zurückgegebenen ImpImporter?) Am saubersten importiert werden können.

30
linkmaster03

Bearbeiten: Hier ist eine Möglichkeit, alles zur Laufzeit rekursiv zu importieren ... 

(Inhalt von __init__.py im obersten Paketverzeichnis)

import pkgutil

__all__ = []
for loader, module_name, is_pkg in  pkgutil.walk_packages(__path__):
    __all__.append(module_name)
    _module = loader.find_module(module_name).load_module(module_name)
    globals()[module_name] = _module

Ich benutze hier nicht __import__(__path__+'.'+module_name), da es schwierig ist, Pakete rekursiv zu importieren. Wenn Sie keine verschachtelten Unterpakete haben und globals()[module_name] nicht verwenden möchten, ist dies eine Möglichkeit.

Es gibt wahrscheinlich einen besseren Weg, aber das ist das Beste, was ich tun kann.

Ursprüngliche Antwort (Im Kontext ignorieren Sie eine andere. Ich habe die Frage zunächst falsch verstanden):

Wie sieht dein scripts/__init__.py aus? Es sollte so etwas wie sein:

import script1
import script2
import script3
__all__ = ['script1', 'script2', 'script3']

Sie können sogar auf __all__ verzichten, aber Dinge (pydoc, wenn nichts anderes) funktionieren sauberer, wenn Sie sie definieren, selbst wenn es sich nur um eine Liste dessen handelt, was Sie importiert haben.

31
Joe Kington

Dies basiert auf der Antwort, die kolypto geliefert hat , aber seine Antwort führt keinen rekursiven Import von Paketen durch, wohingegen dies der Fall ist. Obwohl dies in der Hauptfrage nicht erforderlich ist, glaube ich, dass der rekursive Import zutreffend ist und in vielen ähnlichen Situationen sehr nützlich sein kann. Ich habe diese Frage bei der Suche nach dem Thema gefunden.

Dies ist eine schöne, saubere Methode zum Importieren der Module des Subpakets. Sie sollte auch portabel sein und verwendet die Standard-Bibliothek für Python 2.7+/3.x.

import importlib
import pkgutil


def import_submodules(package, recursive=True):
    """ Import all submodules of a module, recursively, including subpackages

    :param package: package (name or actual module)
    :type package: str | module
    :rtype: dict[str, types.ModuleType]
    """
    if isinstance(package, str):
        package = importlib.import_module(package)
    results = {}
    for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
        full_name = package.__+ '.' + name
        results[full_name] = importlib.import_module(full_name)
        if recursive and is_pkg:
            results.update(import_submodules(full_name))
    return results

Verwendung:

# from main.py, as per the OP's project structure
import scripts
import_submodules(scripts)

# Alternatively, from scripts.__init__.py
import_submodules(__name__)
23
Mr. B

Funktioniert einfach und ermöglicht den relativen Import in Paketen:

def import_submodules(package_name):
    """ Import all submodules of a module, recursively

    :param package_name: Package name
    :type package_name: str
    :rtype: dict[types.ModuleType]
    """
    package = sys.modules[package_name]
    return {
        name: importlib.import_module(package_name + '.' + name)
        for loader, name, is_pkg in pkgutil.walk_packages(package.__path__)
    }

Verwendungszweck:

__all__ = import_submodules(__name__).keys()
12
kolypto

Ich wurde dieses Problems müde und schrieb ein Paket namens automodinit, um das Problem zu beheben. Sie können es von http://pypi.python.org/pypi/automodinit/ erhalten. Die Nutzung ist wie folgt:

  1. Binden Sie das Paket automodinit in Ihre setup.py-Abhängigkeiten ein.
  2. Fügen Sie am Anfang der __init__.py-Datei Folgendes hinzu:
__all__ = ["I will get rewritten"]
# Don't modify the line above, or this line!
import automodinit
automodinit.automodinit(__name__, __file__, globals())
del automodinit
# Anything else you want can go after here, it won't get modified.

Das ist es! Ab sofort wird beim Importieren eines Moduls __all__ auf Eine Liste von .py [co] -Dateien im Modul gesetzt und außerdem jeder Dieser Dateien so importiert, als hätten Sie Folgendes eingegeben:

for x in __all__: import x

Daher stimmt die Wirkung von from M import * genau mit import M überein.

automodinit läuft gerne in Zip-Archiven und ist daher zip-sicher.

2
Niall Douglas

Nicht annähernd so sauber wie ich möchte, aber keine der saubereren Methoden hat für mich funktioniert. Dadurch wird das angegebene Verhalten erreicht:

Verzeichnisaufbau:

| pkg
|--| __init__.py
   | main.py
   | scripts
   |--| __init__.py
      | script1.py
      | script2.py
      | script3.py

Wo pkg/scripts/__init__.py leer ist und pkg/__init__.py enthält:

import importlib as _importlib
import pkgutil as _pkgutil
__all__ = [_mod[1].split(".")[-1] for _mod in
           filter(lambda _mod: _mod[1].count(".") == 1 and not 
                               _mod[2] and __in _mod[1],
                  [_mod for _mod in _pkgutil.walk_packages("." + __name__)])]
__sub_mods__ = [".".join(_mod[1].split(".")[1:]) for _mod in
                filter(lambda _mod: _mod[1].count(".") > 1 and not 
                                    _mod[2] and __in _mod[1],
                       [_mod for _mod in 
                        _pkgutil.walk_packages("." + __name__)])]
from . import *
for _module in __sub_mods__:
    _importlib.import_module("." + _module, package=__name__)

Obwohl es unordentlich ist, sollte es tragbar sein. Ich habe diesen Code für verschiedene Pakete verwendet.

2
user2561747

Ich habe mit Joe Kingtons Answer herumgespielt und/und eine Lösung entwickelt, die globals und get/setattr verwendet und daher keine Bewertung benötigt. Eine geringfügige Änderung ist, dass ich nicht direkt die Pakete __path__ für walk_packages verwende, sondern das übergeordnete Paket der Pakete verwende und dann nur Module importiere, die mit __+ "." beginnen. Dies geschah, um alle Unterpakete zuverlässig von walk_packages abzurufen - in meinem Anwendungsfall hatte ich ein Unterpaket namens test, das pkgutil veranlasste, das test-Paket aus der Bibliothek von python zu durchlaufen; Außerdem würde __path__ nicht in die Paket-Unterverzeichnisse zurückkehren. Alle diese Probleme wurden mit Jython und Python2.5 beobachtet. Der folgende Code wird bisher nur in Jython getestet.

Beachten Sie auch, dass die OPs-Frage nur über den Import aller module aus einem Paket spricht. Dieser Code importiert auch alle Pakete rekursiv. 

from pkgutil import walk_packages
from os import path

__all__ = []
__pkg_prefix = "%s." % __name__
__pkg_path = path.abspath(__path__[0]).rsplit("/", 1)[0] #parent directory

for loader, modname, _ in walk_packages([__pkg_path]):
    if modname.startswith(__pkg_prefix):
        #load the module / package
        module = loader.find_module(modname).load_module(modname)
        modname = modname[len(__pkg_prefix):] #strip package prefix from name
        #append all toplevel modules and packages to __all__
        if not "." in modname:
            __all__.append(modname)
            globals()[modname] = module
        #set everything else as an attribute of their parent package
        else:
            #get the toplevel package from globals()
            pkg_name, rest = modname.split(".", 1)
            pkg = globals()[pkg_name]
            #recursively get the modules parent package via getattr
            while "." in rest:
                subpkg, rest = rest.split(".", 1)
                pkg = getattr(pkg, subpkg)
            #set the module (or package) as an attribute of its parent package
            setattr(pkg, rest, module)

Als zukünftige Verbesserung versuche ich, diese Dynamik mit einem __getattr__-Hook an dem Paket zu gestalten, sodass die tatsächlichen Module nur importiert werden, wenn auf sie zugegriffen wird.

1
l4mpi

Ich schrieb eine kleine persönliche Bibliothek und fügte ständig neue Module hinzu. Deshalb schrieb ich ein Shell-Skript, um nach Skripten zu suchen und die __init__.pys zu erstellen. Das Skript wird außerhalb des Hauptverzeichnisses für mein Paket pylux ausgeführt.

Ich weiß, es ist wahrscheinlich nicht die Antwort, nach der Sie suchen, aber sie dient mir als Zweck und ist möglicherweise auch für andere nützlich.

#!/bin/bash

echo 'Traversing folder hierarchy...'

CWD=`pwd`


for directory in `find pylux -type d -exec echo {} \;`;
do
    cd $directory
    #echo Entering $directory
    echo -n "" > __init__.py

    for subdirectory in `find . -type d -maxdepth 1 -mindepth 1`;
    do
        subdirectory=`echo $subdirectory | cut -b 3-`
        #echo -n '    ' ...$subdirectory
        #echo -e '\t->\t' import $subdirectory
        echo import $subdirectory >> __init__.py
    done

    for pyfile in *.py ;
    do
        if [ $pyfile = $(echo __init__.py) ]; then
            continue
        fi
        #echo -n '    ' ...$pyfile
        #echo -e '\t->\t' import `echo $pyfile | cut -d . -f 1`
        echo import `echo $pyfile | cut -d . -f 1` >> __init__.py
    done
    cd $CWD

done


for directory in `find pylux -type d -exec echo {} \;`;
do
    echo $directory/__init__.py:
    cat $directory/__init__.py | awk '{ print "\t"$0 }'
done
0
physicsmichael

Das funktioniert gut für mich in Python 3.3. Beachten Sie, dass dies nur für Submodule funktioniert, die sich in Dateien im selben Verzeichnis wie der __init__.py befinden. Mit etwas Arbeit kann es jedoch auch für die Unterstützung von Submodulen in Verzeichnissen erweitert werden.

from glob import iglob
from os.path import basename, relpath, sep, splitext

def import_submodules(__path__to_here):
    """Imports all submodules.
    Import this function in __init__.py and put this line to it:
    __all__ = import_submodules(__path__)"""
    result = []
    for smfile in iglob(relpath(__path__to_here[0]) + "/*.py"):
        submodule = splitext(basename(smfile))[0]
        importstr = ".".join(smfile.split(sep)[:-1])
        if not submodule.startswith("_"):
            __import__(importstr + "." + submodule)
            result.append(submodule)
    return result
0
SzieberthAdam

In Python 3 können Sie den folgenden Code in Ihre scripts.__init__.py-Datei einfügen:

import os
import os.path as op

__all__ = [
    op.splitext(f)[0]  # remove .py extension
    for f in os.listdir(BASE_DIR)  # list contents of current dir
    if not f.startswith('_') and
    ((op.isfile(op.join(BASE_DIR, f)) and f.endswith('.py')) or
     (op.isdir(op.join(BASE_DIR, f)) and op.isfile(op.join(BASE_DIR, f, '__init__.py'))))
]

from . import *  # to make `scripts.script1` work after `import script`

Für weitere Informationen zu Python-Importen empfehle ich David Beazleys Vortrag auf der PyCon 2015: https://youtu.be/0oTh1CXRaQ0

0
ostrokach