次のようなディレクトリ構造があります。
| 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?)はわかりません。それらをインポートします。
編集:これは、実行時にすべてを再帰的にインポートする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がより適切に機能します。
これは 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__)
単純に機能し、パッケージ内での相対的なインポートを可能にします。
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()
私は自分でこの問題に飽きたので、automodinitというパッケージを作成して修正しました。 http://pypi.python.org/pypi/automodinit/ から取得できます。使い方は次のとおりです:
setup.py
依存関係に含めます。__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セーフです。
思ったほどきれいではありませんが、クリーナーの方法はどれもうまくいきませんでした。これにより、指定された動作が実現します。
ディレクトリ構造:
| 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__)
面倒ですが、移植可能でなければなりません。このコードをいくつかの異なるパッケージに使用しました。
私は Joe Kington's Answer をいじって、globals
とget/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__
フックを使用してこれを動的にしようとするので、実際のモジュールは、アクセスされたときにのみインポートされます...
私は小さな個人用ライブラリを作成し、常に新しいモジュールを追加していたため、スクリプトを探して__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
これは、私にとって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
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