Jak zaimportować wszystkie moduły podrzędne?

Mam następującą strukturę katalogów:

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

Z main.py importowany jest moduł scripts. Próbowałem używać pkgutils.walk_packages w połączeniu z __all__, ale używając tego, mogę importować wszystkie moduły bezpośrednio pod main używając from scripts import *. Chciałbym je wszystkie umieścić pod scripts. Jaki byłby najczystszy sposób zaimportowania wszystkich podmodułów scripts, abym mógł uzyskać dostęp scripts.script1 z main?

EDIT: przepraszam, że byłem trochę niejasny. Chciałbym zaimportować podmoduły na run-time bez podania ich wprost w __init__.py. Mogę użyć pkgutils.walk_packages, aby uzyskać nazwy podmodułów (chyba, że ktoś zna lepszy sposób), ale nie jestem pewien najczystszego sposobu użycia tych nazw (a może Impiporterów, które walk_packages zwraca?), aby je zaimportować.

Author: linkmaster03, 2010-07-29

9 answers

Edit: Oto jeden ze sposobów rekurencyjnego importowania wszystkiego w czasie wykonywania...

(Zawartość {[2] } w katalogu top package)

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

Nie używam tutaj __import__(__path__+'.'+module_name), ponieważ trudno jest poprawnie zaimportować za jego pomocą Pakiety. Jeśli nie masz zagnieżdżonych podpakietów i chcesz uniknąć używania globals()[module_name], jest to jeden ze sposobów, aby to zrobić.

Prawdopodobnie jest lepszy sposób, ale to najlepsze, co mogę zrobić.

Oryginalna odpowiedź (dla kontekstu, zignoruj othwerwise. Początkowo źle zrozumiałem pytanie):

Jak wygląda twój scripts/__init__.py? Powinno być coś w stylu:

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

Można nawet zrobić bez zdefiniowania __all__, ale rzeczy (pydoc, jeśli nic innego) będą działać bardziej czysto, jeśli go zdefiniujesz, nawet jeśli jest to tylko lista tego, co zaimportowałeś.

 27
Author: Joe Kington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 11:24:15

Jest to oparte na odpowiedzi, którą dostarczył kolypto, ale jego odpowiedź nie wykonuje rekurencyjnego importu pakietów, podczas gdy ta. Chociaż nie jest to wymagane przez główne pytanie, uważam, że import rekurencyjny ma zastosowanie i może być bardzo przydatny w wielu podobnych sytuacjach. Ja, na przykład, znalazłem to pytanie podczas wyszukiwania na ten temat.

Jest to przyjemny, czysty sposób na zaimportowanie modułów podpakietu, powinien być również przenośny i używa standardowego lib dla 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__ + '.' + name
        results[full_name] = importlib.import_module(full_name)
        if recursive and is_pkg:
            results.update(import_submodules(full_name))
    return results

Użycie:

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

# Alternatively, from scripts.__init__.py
import_submodules(__name__)
 21
Author: Mr. B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:20

Po prostu działa i umożliwia względny import wewnątrz pakietów:

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__)
    }

Użycie:

__all__ = import_submodules(__name__).keys()
 11
Author: kolypto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-01 15:03:59

Nie tak czysto, jak bym chciała, ale żadna z czystszych metod nie zadziałała. Uzyskuje się w ten sposób określone zachowanie:

Struktura katalogu:

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

Gdzie pkg/scripts/__init__.py jest puste, a pkg/__init__.py zawiera:

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 __name__ 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 __name__ in _mod[1],
                       [_mod for _mod in 
                        _pkgutil.walk_packages("." + __name__)])]
from . import *
for _module in __sub_mods__:
    _importlib.import_module("." + _module, package=__name__)
Chociaż jest brudny, powinien być przenośny. Użyłem tego kodu dla kilku różnych pakietów.
 2
Author: user2561747,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-10 17:46:41

Sam miałem dość tego problemu, więc napisałem pakiet o nazwie automodinit, aby go naprawić. Możesz go pobrać z http://pypi.python.org/pypi/automodinit / . użycie wygląda tak:

  1. Dołącz pakiet automodinit do swoich zależności setup.py.
  2. Dodaj następujący tekst na początku pliku __init__.py:
__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.
To jest to! Od teraz importowanie modułu ustawia __all__ na listę plików. py [co] w module, a także zaimportuje każdy z tych pliki tak jakbyś wpisał:
for x in __all__: import x

Dlatego efekt from M import * pasuje dokładnie import M.

Automodinit jest zadowolony z działania z archiwów ZIP i dlatego jest bezpieczny dla ZIP.

 2
Author: Niall Douglas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-28 20:31:58

Pisałem małą bibliotekę osobistą i cały czas dodawałem nowe moduły, więc napisałem skrypt powłoki, aby szukać skryptów i tworzyć __init__.py ' s. skrypt jest uruchamiany tuż poza głównym katalogiem mojego pakietu, pylux.

Wiem, że prawdopodobnie nie jest to odpowiedź, której szukasz, ale to servered swój cel dla mnie i może być przydatna dla kogoś innego, zbyt.

#!/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
 1
Author: physicsmichael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-29 18:59:18

Bawiłem się odpowiedzią Joe Kingtona i zbudowałem rozwiązanie, które wykorzystuje globals i get/setattr, a więc nie potrzebuje evalu. Drobna modyfikacja polega na tym, że zamiast bezpośrednio używać packages __path__ dla walk_packages, używam katalogu nadrzędnego packages, a następnie importuję tylko Moduły zaczynające się od __name__ + ".". Zostało to zrobione, aby niezawodnie pobrać wszystkie podpakiety z walk_packages - w moim przypadku użycia miałem podpakiet o nazwie test, który spowodował, że pkgutil iterował nad test pakietem z Pythona biblioteka; ponadto użycie __path__ nie spowoduje rekurencji do podkatalogów packages. Wszystkie te problemy zostały zaobserwowane przy użyciu jython i python2. 5, poniższy kod jest testowany tylko w jython do tej pory.

Zauważ również, że pytanie OPs mówi tylko o importowaniu wszystkich modułów z pakietu, ten kod rekurencyjnie importuje również wszystkie pakiety.

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)

Jako przyszłe ulepszenie postaram się wprowadzić tę dynamikę za pomocą __getattr__ Hooka na pakiecie, więc rzeczywiste moduły są tylko importowane, gdy są dostępne...

 1
Author: l4mpi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:23

To działa mi ładnie w Pythonie 3.3. Zauważ, że działa to tylko dla podmoduł, które znajdują się w plikach w tym samym katalogu co __init__.py. Z pewną pracą można go jednak ulepszyć do obsługi podmoduł w katalogach.

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
Author: SzieberthAdam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-02 06:47:24

W Pythonie 3, możesz umieścić następujący kod w pliku scripts.__init__.py:

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`

Aby uzyskać więcej informacji na temat importu Pythona, polecam wystąpienie Davida Beazleya na PyCon 2015: https://youtu.be/0oTh1CXRaQ0

 0
Author: ostrokach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-08 20:06:24