web-dev-qa-db-ja.com

すべてのサブモジュールをインポートする方法は?

次のようなディレクトリ構造があります。

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

main.pyから、モジュールscriptsがインポートされます。 pkgutils.walk_packages__all__と組み合わせて使用​​してみましたが、それを使用すると、from scripts import *を使用してmainの直下にあるすべてのサブモジュールしかインポートできません。それらすべてをscriptsの下で取得したいと思います。 scriptsからscripts.script1にアクセスできるように、mainのすべてのサブモジュールをインポートする最もクリーンな方法は何ですか?

編集:私は少し漠然としていたことを残念に思います。 __init__.pyで明示的に指定せずに、実行時にサブモジュールをインポートしたいと思います。 pkgutils.walk_packagesを使用してサブモジュール名を取得できます(誰かがより良い方法を知らない限り)が、これらの名前を使用する最もクリーンな方法(またはwalk_packagesが返すImpImporters?)はわかりません。それらをインポートします。

38
linkmaster03

編集:これは、実行時にすべてを再帰的にインポートする1​​つの方法です...

(最上位パッケージディレクトリの___init__.py_の内容)

_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
_

ここでは__import__(__path__+'.'+module_name)を使用していません。適切に再帰的にパッケージをインポートすることは難しいためです。ネストされたサブパッケージがなく、globals()[module_name]の使用を避けたい場合は、これが1つの方法です。

おそらくもっと良い方法がありますが、とにかくこれが私ができる最善の方法です。

元の回答(コンテキストについては、それ以外は無視してください。最初に質問を誤解しました):

_scripts/__init__.py_はどのように見えますか?次のようになります。

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

___all___を定義せずに実行することもできますが、インポートしたもののリストであっても、それを定義すると(他に何もない場合でも)pydocがより適切に機能します。

38
Joe Kington

これは kolyptoが提供した答え に基づいていますが、彼の答えはパッケージの再帰的なインポートを実行しませんが、これは実行します。メインの質問では必要ありませんが、再帰的なインポートが適用され、多くの同様の状況で非常に役立つと思います。私は、トピックを検索しているときに、この質問を見つけました。

これは、サブパッケージのモジュールのインポートを実行するすてきでクリーンな方法であり、移植性も必要です。また、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

用途:

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

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

単純に機能し、パッケージ内での相対的なインポートを可能にします。

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

使用法:

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

私は自分でこの問題に飽きたので、automodinitというパッケージを作成して修正しました。 http://pypi.python.org/pypi/automodinit/ から取得できます。使い方は次のとおりです:

  1. Automodinitパッケージをsetup.py依存関係に含めます。
  2. 以下を__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.

それでおしまい!これ以降、モジュールをインポートすると、__all__がモジュール内の.py [co]ファイルのリストに設定され、入力したかのようにそれらの各ファイルもインポートされます。

for x in __all__: import x

したがって、from M import *の効果はimport Mと完全に一致します。

automodinitは、Zipアーカイブ内から実行できるため、Zipセーフです。

2
Niall Douglas

思ったほどきれいではありませんが、クリーナーの方法はどれもうまくいきませんでした。これにより、指定された動作が実現します。

ディレクトリ構造:

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

どこ pkg/scripts/__init__.pyは空で、pkg/__init__.pyに含まれるもの:

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

面倒ですが、移植可能でなければなりません。このコードをいくつかの異なるパッケージに使用しました。

2
user2561747

私は Joe Kington's Answer をいじって、globalsget/setattrを使用するソリューションを構築しました。そのため、evalは必要ありません。わずかな変更は、__path__に直接パッケージwalk_packagesを使用する代わりに、パッケージの親ディレクトリを使用して、__name__ + "."で始まるモジュールのみをインポートすることです。これは、walk_packagesからすべてのサブパッケージを確実に取得するために行われました-私の使用例では、testという名前のサブパッケージがあり、pkgutilがpythonのライブラリからtestパッケージを反復処理しました。さらに、__path__を使用しても、パッケージのサブディレクトリに再帰しません。これらの問題はすべてjythonとpython2.5を使用して観察されました。以下のコードは、これまでjythonでのみテストされています。

また、OPの質問では、パッケージからすべてのmodulesをインポートすることについてのみ話していることに注意してください。このコードは、すべてのパッケージを再帰的にインポートします。

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)

今後の改善点として、パッケージの__getattr__フックを使用してこれを動的にしようとするので、実際のモジュールは、アクセスされたときにのみインポートされます...

1
l4mpi

私は小さな個人用ライブラリを作成し、常に新しいモジュールを追加していたため、スクリプトを探して__init__.pyを作成するシェルスクリプトを作成しました。スクリプトは、私のパッケージpyluxのメインディレクトリのすぐ外で実行されます。

それがおそらくあなたが探している答えではないことは知っていますが、それは私にとってその目的を果たしており、他の誰かにも役立つかもしれません。

#!/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

これは、私にとってPython 3.3でうまく機能します。これは、__init__.pyと同じディレクトリのファイルにあるサブモジュールでのみ機能することに注意してください。ディレクトリ内のサブモジュールもサポートします。

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

Python 3では、次のコードを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`

Pythonインポートの詳細については、PyCon 2015でのDavid Beazleyの講演をお勧めします: https://youtu.be/0oTh1CXRaQ

0
ostrokach