关于python:如何在一个文件夹中加载所有模块?

How to load all modules in a folder?

有人能为我提供一个导入整个模块目录的好方法吗?我有这样的结构:

1
2
3
4
/Foo
    bar.py
    spam.py
    eggs.py

我试图通过添加__init__.py和执行from Foo import *,将其转换为一个包,但没有按我希望的方式工作。


列出当前文件夹中的所有python(.py文件,并将其作为__init__.py中的__all__变量。

1
2
3
4
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')]


__all__变量添加到__init__.py中,其中包含:

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

另请参见http://docs.python.org/tutorial/modules.html


更新:今天您可能想使用importlib

通过添加__init__.py使foo目录成为一个包。其中__init__.py增加:

1
2
3
import bar
import eggs
import spam

由于您希望它是动态的(这可能是一个好主意,也可能不是一个好主意),请用list dir列出所有py文件,然后用如下方式导入它们:

1
2
3
4
5
6
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

然后,根据您的代码执行以下操作:

1
import Foo

您现在可以使用

1
2
3
Foo.bar
Foo.eggs
Foo.spam

等等,从foo导入*不是一个好主意,原因有几个,包括名称冲突和使代码难以分析。


扩展Mihail的答案,我认为非黑客方式(如,不直接处理文件路径)如下:

  • Foo/下创建一个空的__init__.py文件
  • 执行
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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')

    你会得到:

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


    python,包括目录下的所有文件:

    对于那些不能让它工作的新手来说,他们需要双手紧握。

  • 创建一个文件夹/home/el/foo,并在/home/el/foo下创建一个文件main.py,将此代码放入其中:

    1
    2
    3
    from hellokitty import *
    spam.spamfunc()
    ham.hamfunc()
  • 制作目录/home/el/foo/hellokitty

  • /home/el/foo/hellokitty下生成文件__init__.py并将此代码放入其中:

    1
    __all__ = ["spam","ham"]
  • 生成两个python文件:spam.pyham.py/home/el/foo/hellokitty下。

  • 在spam.py中定义函数:

    1
    2
    def spamfunc():
      print"Spammity spam"
  • 在ham.py中定义函数:

    1
    2
    def hamfunc():
      print"Upgrade from baloney"
  • 运行它:

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

  • 我自己也厌倦了这个问题,所以我写了一个叫做automodinit的包来解决它。您可以从http://pypi.python.org/pypi/automodinit/获得它。

    用法如下:

  • automodinit包包含到您的setup.py依赖项中。
  • 替换所有的初始化文件,如下所示:
  • 1
    2
    3
    4
    5
    6
    __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.

    就是这样!从现在起,导入模块将设置为模块中的.py[co]文件列表,还将导入每个文件在这些文件中,您好像键入了:

    1
    for x in __all__: import x

    因此,"从M导入*"的效果与"导入M"完全匹配。

    automodinit很高兴从zip文件中运行,因此是zip安全的。

    尼尔


    我知道我正在更新一个相当老的帖子,我尝试使用automodinit,但发现python3的设置过程被破坏了。所以,基于卢卡的回答,我想出了一个更简单的答案——可能不适用于.zip——来解决这个问题,所以我想我应该在这里分享它:

    在来自yourpackage__init__.py模块内:

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

    yourpackage下面的另一个包中:

    1
    from yourpackage import *

    然后,您将加载放置在包中的所有模块,如果编写一个新模块,它也将自动导入。当然,谨慎地使用这类东西,拥有强大的权力就有巨大的责任。


    我也遇到了这个问题,这是我的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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的文件夹其中包含:

    1
    2
    Foo.py
    Bar.py

    因此,在脚本中,我希望将模块导入其中,我将写下:

    1
    2
    loadImports('Test/')
    from Test import *

    这将从Test导入所有内容,Test中的__init__.py文件现在将包含:

    1
    __all__ = ['Foo','Bar']


    Anurag Uniyal给出了建议改进的答案!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #!/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

    Anurag的例子中有几个修正:

    1
    2
    3
    4
    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")]

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


    这是迄今为止我发现的最好的方法:

    1
    2
    3
    4
    5
    6
    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())

    注意你的__init__.py定义了__all__。模块-包文档说

    The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

    ...

    The only solution is for the package author to provide an explicit index of the package. The import statement uses the following convention: if a package’s __init__.py code defines a list named __all__, it is taken to be the list of module names that should be imported when from package import * is encountered. It is up to the package author to keep this list up-to-date when a new version of the package is released. Package authors may also decide not to support it, if they don’t see a use for importing * from their package. For example, the file sounds/effects/__init__.py could contain the following code:

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

    This would mean that from sound.effects import * would import the three named submodules of the sound package.


    我已经为它创建了一个模块,它不依赖于__init__.py或任何其他辅助文件,并且使我只键入以下两行:

    1
    2
    import importdir
    importdir.do("Foo", globals())

    请随意重复使用或贡献:http://gitlab.com/aurelien-lourot/importdir


    从标准库中查看pkgutil模块。只要目录中有一个__init__.py文件,它就可以让您做您想要做的事情。__init__.py文件可以为空。


    只需通过importlib导入它们,并将它们添加到__all__(add操作是可选的),以便在包的__init__.py中重复使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /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