フォルダ内のすべてのモジュールをロードする方法は?


269

誰かがモジュールのディレクトリ全体をインポートする良い方法を私に提供できますか?
私はこのような構造を持っています:

/Foo
    bar.py
    spam.py
    eggs.py

追加__init__.pyして実行するfrom Foo import *だけでパッケージに変換しようとしましたが、期待したとおりに機能しませんでした。


2
INITの.py方法はかなり正確です。機能しなかったコードを投稿できますか?
balpha 2009年

9
「機能しなかった」と定義できますか?どうした?どのエラーメッセージが表示されましたか?
S.Lott、2009年

これはPythonicまたは推奨ですか? 「明示的は暗黙的よりも優れています。」
yangmillstheory

明示は確かに優れています。しかし、新しいモジュールを追加するたびに、これらの迷惑な「見つかりません」というメッセージに対処したいと思います。多くの小さなモジュール関数を含むパッケージディレクトリがある場合、これはそれを行うための最も良い方法だと思います。私の仮定は次のとおりです。1.モジュールはかなり単純です2.コードを整頓するためにパッケージを使用します
Ido_f

回答:


400

.py現在のフォルダー内のすべてのpython()ファイルをリストし、それらを__all__変数として__init__.py

from os.path import dirname, basename, isfile, join
import glob
modules = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]

57
基本的には、Pythonファイルを追加の構成なしでディレクトリにドロップし、他の場所で実行されているスクリプトによって実行させることができます。
エヴァンフォスマルク2009年

5
@NiallDouglasこの回答は、OPが尋ねた特定の質問に対するもので、zipファイルがなく、pycファイルを簡単に含めることができ、.pydや.so libsなども忘れてしまいます
Anurag Uniyal

35
私が追加する唯一のことは、if not os.path.basename(f).startswith('_')または少なくとも少なくともif not f.endswith('__init__.py')リストの理解の終わりまでです
Pykler

10
より堅牢にするために、であることも確認しos.path.isfile(f)てくださいTrue。これは、壊れたシンボリックリンクやディレクトリsomedir.py/
たとえば

12
追加from . import *設定した後、__all__あなたはサブモジュールを使用して利用できるようにしたい場合.(例えばとしてmodule.submodule1module.submodule2など)。
ostrokach

133

以下__all____init__.py含む変数を追加します。

__all__ = ["bar", "spam", "eggs"]

http://docs.python.org/tutorial/modules.htmlも参照してください


163
はい、はい、しかしそれを動的にする方法はありますか?
エヴァンフォスマルク2009年

27
os.listdir()、いくつかのフィルタリング、.py拡張子の除去、およびの組み合わせ__all__

ここでサンプルコードとして私のために働いていませんgithub.com/namgivu/python-import-all/blob/master/error_app.py。たぶん私はそこに何かを見逃していますか?
Nam G VU 2017年

1
私は自分自身を見つけました-それらのモジュールで定義された変数/オブジェクトを使用するには、完全な参照パス、たとえばmoduleName.varNameref を使用する必要があります。stackoverflow.com/a/710603/248616
Nam G VU

1
@NamGVU:関連する質問に対する私の回答のこのコードはすべてのパブリックサブモジュールの名前をパッケージの名前空間にインポートします。
martineau 2017年

54

2017年の更新:おそらくimportlib代わりに使用したいでしょう。

を追加して、Fooディレクトリをパッケージにし__init__.pyます。その__init__.py追加:

import bar
import eggs
import spam

あなたはそれを動的にしたいので(それは良い考えかもしれないしそうでないかもしれません)、リストdirですべてのpyファイルをリストし、次のようなものでそれらをインポートしてください:

import os
for module in os.listdir(os.path.dirname(__file__)):
    if module == '__init__.py' or module[-3:] != '.py':
        continue
    __import__(module[:-3], locals(), globals())
del module

次に、コードから次のようにします。

import Foo

これでモジュールにアクセスできます

Foo.bar
Foo.eggs
Foo.spam

from Foo import *名前の衝突やコードの分析が困難になるなど、いくつかの理由から、などはお勧めできません。


1
悪くありませんが、.pycファイルと.pyoファイルもインポートできることを忘れないでください。
エヴァンフォスマルク2009年

7
tbh、私は__import__ハックっぽいので、名前を追加してスクリプトの最後に__all__置く方がいいと思いfrom . import *ます
freeforall tousez '26 / 08/26

1
これはglobバージョンよりも良いと思います。
lpapp

8
__import__は一般的な用途ではなく、によって使用されinterpreterますimportlib.import_module()。代わりに使用してください。
Andrew_1510 2016年

2
最初の例はとても役に立ちました、ありがとう!Python 3.6.4 from . import eggsでは、__init__.pyPythonがインポートできるようになる前に、私は等をしなければなりませんでした。上のディレクトリでしようとすると、import eggs私だけが取得ModuleNotFoundError: No module named 'eggs'します。import Foomain.py
Nick

41

Mihailの答えを拡張すると、ハッキングをしない方法(ファイルパスを直接処理しないなど)は次のようになります。

  1. __init__.py下に空のファイルを作成するFoo/
  2. 実行する
import pkgutil
import sys


def load_all_modules_from_dir(dirname):
    for importer, package_name, _ in pkgutil.iter_modules([dirname]):
        full_package_name = '%s.%s' % (dirname, package_name)
        if full_package_name not in sys.modules:
            module = importer.find_module(package_name
                        ).load_module(full_package_name)
            print module


load_all_modules_from_dir('Foo')

あなたは得るでしょう:

<module 'Foo.bar' from '/home/.../Foo/bar.pyc'>
<module 'Foo.spam' from '/home/.../Foo/spam.pyc'>

これが正解のほとんどの方法です。ZIPアーカイブを処理しますが、initやimport は書き込みません。以下のautomodinitを参照してください。
Niall Douglas

1
もう1つ:上記の例では、sys.modulesをチェックして、モジュールがすでにロードされているかどうかを確認していません。そのチェックがないと、上記はモジュールを
もう一度

3
コードでload_all_modules_from_dir( 'Foo / bar')を実行すると、「RuntimeWarning:Parent module 'Foo / bar' not found while while absolute import」が表示されます-これを抑制するには、full_package_name = '。'。join( dirname.split(os.path.sep)+ package_name])およびFoo.barもインポート
Alex Dupuy

これらのRuntimeWarningメッセージは、full_package_nameをまったく使用しないことでも回避できますimporter.find_module(package_name).load_module(package_name)
Artfunkel 2016年

RuntimeWarningエラーは、親(AKAのDIRNAME)をインポートすることにより、(ほぼ間違いなく醜い方法で)を回避することができます。これを行う1つの方法は- if dirname not in sys.modules: pkgutil.find_loader(dirname).load_module(dirname)です。もちろん、これdirnameは単一コンポーネントの相対パスの場合にのみ機能します。スラッシュはありません。個人的には、代わりにベースのpackage_nameを使用する@Artfunkelのアプローチを好みます。
dannysauer 2017年

31

Python、ディレクトリの下にすべてのファイルを含める:

手を握る必要があるだけで仕事を始められない初心者向け。

  1. / home / el / fooフォルダーを作成し、main.py/ home / el / fooの下にファイルを作成します。

    from hellokitty import *
    spam.spamfunc()
    ham.hamfunc()
  2. ディレクトリを作る /home/el/foo/hellokitty

  3. __init__.py下にファイルを作成し/home/el/foo/hellokitty、このコードをそこに配置します。

    __all__ = ["spam", "ham"]
  4. 2つのpythonファイルを作成しますspam.pyham.py/home/el/foo/hellokitty

  5. spam.py内で関数を定義します。

    def spamfunc():
      print("Spammity spam")
  6. ham.py内で関数を定義します。

    def hamfunc():
      print("Upgrade from baloney")
  7. それを実行します:

    el@apollo:/home/el/foo$ python main.py 
    spammity spam
    Upgrade from baloney

1
初心者だけでなく、明確な答えを好む経験豊富なPython開発者も対象です。ありがとう。
Michael Scheper

しかし、使用import *はPythonのコーディング慣行としては好ましくないと見なされています。それなしでこれをどのように行うのですか?
Rachael Blake

16

私は自分でこの問題に飽きたので、それを修正するためにautomodinitというパッケージを書きました。http://pypi.python.org/pypi/automodinit/から取得できます。

使い方は次のとおりです:

  1. automodinitパッケージをsetup.py依存関係に含めます。
  2. 次のようにすべての__init__.pyファイルを置き換えます。
__all__ = [「書き直します」]
#上記の行またはこの行を変更しないでください!
automodinitのインポート
automodinit.automodinit(__ name__、__file__、globals())
del automodinit
#他に必要なものはここから移動できますが、変更されません。

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

for x in __all__: import x

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

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

ニアル


pypiにアップロードされたものがないため、pipはautomodinitをダウンロードできません。
kanzure

githubのバグレポートをありがとう。これをv0.13で修正しました。Niall
Niall Douglas

11

かなり古い投稿を更新していることを知っているので、を使用automodinitしてみましたが、python3のセットアッププロセスが壊れていることがわかりました。だから、ルカの答えに基づいて、私はこの問題に対してより簡単な答え-.zipでは機能しないかもしれない-を思いついたので、私はここでそれを共有する必要があると考えました:

__init__.pyモジュール内yourpackage

#!/usr/bin/env python
import os, pkgutil
__all__ = list(module for _, module, _ in pkgutil.iter_modules([os.path.dirname(__file__)]))

以下の別のパッケージ内yourpackage

from yourpackage import *

次に、パッケージ内に配置されたすべてのモジュールが読み込まれ、新しいモジュールを作成すると、自動的に自動的にインポートされます。もちろん、そのようなことを注意深く使用することには、大きな力があり、大きな責任が伴います。


6
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
  __import__(module)

5

私もこの問題に遭遇し、これが私の解決策でした:

import os

def loadImports(path):
    files = os.listdir(path)
    imps = []

    for i in range(len(files)):
        name = files[i].split('.')
        if len(name) > 1:
            if name[1] == 'py' and name[0] != '__init__':
               name = name[0]
               imps.append(name)

    file = open(path+'__init__.py','w')

    toWrite = '__all__ = '+str(imps)

    file.write(toWrite)
    file.close()

この関数は、(指定されたフォルダーに)という名前のファイルを作成します。このファイルには、フォルダー内のすべてのモジュールを保持__init__.pyする__all__変数が含まれています。

たとえば、次の名前のフォルダがTest あります。

Foo.py
Bar.py

そのため、スクリプトにモジュールをインポートしたいので、次のように記述します。

loadImports('Test/')
from Test import *

これによりTest、すべてがインポートされ、__init__.pyファイルにTestは以下が含まれます。

__all__ = ['Foo','Bar']

完璧、正確に私が見ているもの
uma mahesh 2018年

4

いくつかの修正を加えたAnuragの例:

import os, glob

modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py"))
__all__ = [os.path.basename(f)[:-3] for f in modules if not f.endswith("__init__.py")]

4

Anurag Uniyalの回答と提案された改善!

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import os
import glob

all_list = list()
for f in glob.glob(os.path.dirname(__file__)+"/*.py"):
    if os.path.isfile(f) and not os.path.basename(f).startswith('_'):
        all_list.append(os.path.basename(f)[:-3])

__all__ = all_list  

3

あなたの__init__.py定義を参照してください__all__モジュール-パッケージドキュメントは言います

これらの__init__.pyファイルは、Pythonがディレクトリをパッケージを含むものとして扱うために必要です。これは、文字列などの一般的な名前のディレクトリが、後でモジュール検索パスで発生する有効なモジュールを誤って非表示にしないようにするために行われます。最も単純なケースで__init__.pyは、空のファイルにすることができ__all__ますが、後で説明するように、パッケージの初期化コードを実行したり、変数を設定したりすることもできます。

...

唯一の解決策は、パッケージ作成者がパッケージの明示的なインデックスを提供することです。importステートメントは次の規則を使用します。パッケージの__init__.pyコードがという名前のリストを定義する__all__場合、from package import *が検出されたときにインポートする必要があるモジュール名のリストと見なされます。パッケージの新しいバージョンがリリースされたときにこのリストを最新に保つのはパッケージ作成者の責任です。パッケージの作成者は、パッケージから*をインポートする使用法が見当たらない場合、サポートしないことを決定することもあります。たとえば、ファイルsounds/effects/__init__.pyには次のコードを含めることができます。

__all__ = ["echo", "surround", "reverse"]

これはfrom sound.effects import *、サウンドパッケージの3つの名前付きサブモジュールをインポートすることを意味します。


3

これは私がこれまでに見つけた最良の方法です:

from os.path import dirname, join, isdir, abspath, basename
from glob import glob
pwd = dirname(__file__)
for x in glob(join(pwd, '*.py')):
    if not x.startswith('__'):
        __import__(basename(x)[:-3], globals(), locals())

2

使用してimportlib追加するんだ唯一の事をされます

from importlib import import_module
from pathlib import Path

__all__ = [
    import_module(f".{f.stem}", __package__)
    for f in Path(__file__).parent.glob("*.py")
    if "__" not in f.stem
]
del import_module, Path

これは正当なmypy問題につながります:error: Type of __all__ must be "Sequence[str]", not "List[Module]"__all__このimport_moduleベースのアプローチを使用する場合、定義は必要ありません。
Acumenus

1

標準ライブラリのpkgutilモジュールを見てください。__init__.pyディレクトリにファイルがある限り、必要な操作を正確に実行できます。__init__.pyファイルは空にすることができます。



0

それらをimportlibによってインポートし、パッケージのに再帰的に__all__addアクションはオプションです)に追加し__init__.pyます。

/Foo
    bar.py
    spam.py
    eggs.py
    __init__.py

# __init__.py
import os
import importlib
pyfile_extes = ['py', ]
__all__ = [importlib.import_module('.%s' % filename, __package__) for filename in [os.path.splitext(i)[0] for i in os.listdir(os.path.dirname(__file__)) if os.path.splitext(i)[1] in pyfile_extes] if not filename.startswith('__')]
del os, importlib, pyfile_extes

pyfile_extesはどこに定義されていますか?
Jeppe、2018年

欠落して申し訳ありませんが、現在は修正されています。これは、インポートしたいPythonファイルの拡張です。通常はちょうどpy
Cheney

0

from . import *が十分でない場合、これはtedによる回答よりも優れています。特に、__all__このアプローチではの使用は必要ありません。

"""Import all modules that exist in the current directory."""
# Ref https://stackoverflow.com/a/60861023/
from importlib import import_module
from pathlib import Path

for f in Path(__file__).parent.glob("*.py"):
    module_name = f.stem
    if (not module_name.startswith("_")) and (module_name not in globals()):
        import_module(f".{module_name}", __package__)
    del f, module_name
del import_module, Path

これmodule_name not in globals()は、モジュールが既にインポートされている場合、再インポートを回避することを目的としていることに注意してください。これは、循環インポートの危険を冒す可能性があるためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.