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

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

/Foo
bar.py
spam.py
eggs.py

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

373752 次浏览

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

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

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

注意你的__init__.py定义了__all__模块—包文件说

需要__init__.py文件才能使Python将目录视为包含包;这样做是为了防止具有通用名称的目录(如string)在无意中隐藏之后在模块搜索路径上出现的有效模块。在最简单的情况下,__init__.py可以只是一个空文件,但它也可以执行包的初始化代码或设置__all__变量,后面将介绍。

...

唯一的解决方案是包作者提供包的显式索引。import语句使用以下约定:如果包的__init__.py代码定义了一个名为__all__的列表,则它将被认为是在遇到from package import *时应该导入的模块名列表。当发布包的新版本时,由包的作者来保持这个列表是最新的。包作者也可能决定不支持它,如果他们看不到从包中导入*的用途。例如,文件sounds/effects/__init__.py可以包含以下代码:

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

这意味着from sound.effects import *将导入声音包的三个命名子模块。

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

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')]

2017年更新:你可能想用importlib代替。

通过添加__init__.py使Foo目录成为一个包。在__init__.py中添加:

import bar
import eggs
import spam

因为你希望它是动态的(这可能是也可能不是一个好主意),用list dir列出所有的py-files,然后像这样导入它们:

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 *不是一个好主意,原因有几个,包括名称冲突和很难分析代码。

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

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

  1. Foo/下创建一个空的__init__.py文件
  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'>

我自己也厌倦了这个问题,所以我写了一个名为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.
< p >就是这样!从现在开始导入一个模块将设置__all__为 模块中的.py[co]文件列表,也将导入每个文件 就好像你输入了:

for x in __all__: import x

因此,“from M import *”的效果与“import M”完全匹配。

automodinit是从ZIP档案中运行的,因此是ZIP安全的。

尼尔

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

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的文件夹 其中包含:< / p >
Foo.py
Bar.py

所以在脚本中,我想把模块导入,我会写:

loadImports('Test/')
from Test import *

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

__all__ = ['Foo','Bar']

包含一个目录下的所有文件:

专为那些无法上手的新手准备的。

  1. 创建一个文件夹/home/el/foo,并在/home/el/foo下创建一个文件main.py

    from hellokitty import *
    spam.spamfunc()
    ham.hamfunc()
    
  2. Make a directory /home/el/foo/hellokitty

  3. Make a file __init__.py under /home/el/foo/hellokitty and put this code in there:

    __all__ = ["spam", "ham"]
    
  4. Make two python files: spam.py and ham.py under /home/el/foo/hellokitty

  5. Define a function inside spam.py:

    def spamfunc():
    print("Spammity spam")
    
  6. Define a function inside ham.py:

    def hamfunc():
    print("Upgrade from baloney")
    
  7. Run it:

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

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

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

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(或任何其他辅助文件),并使我只输入以下两行:

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

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

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")]

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

yourpackage__init__.py模块中:

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

yourpackage下面的另一个包中:

from yourpackage import *

然后,您将加载包中放置的所有模块,如果您编写了一个新模块,它也将被自动导入。当然,要小心使用这些东西,能力越大责任越大。

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

只需通过importlib导入它们,并在包的__init__.py中递归地将它们添加到__all__ (add动作是可选的)。

/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

使用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

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()旨在避免在已经导入模块时重新导入模块,因为这可能存在循环导入的风险。

我有一个嵌套的目录结构,即在包含python模块的主目录中有多个目录。

我在__init__.py文件中添加了以下脚本以导入所有模块

import glob, re, os


module_parent_directory = "path/to/the/directory/containing/__init__.py/file"


owd = os.getcwd()
if not owd.endswith(module_parent_directory): os.chdir(module_parent_directory)


module_paths = glob.glob("**/*.py", recursive = True)


for module_path in module_paths:
if not re.match( ".*__init__.py$", module_path):
import_path = module_path[:-3]
import_path = import_path.replace("/", ".")
exec(f"from .{import_path} import *")


os.chdir(owd)

也许这不是实现这一目标的最佳方式,但除此之外我没有别的办法。

我想添加到Anurag Uniyal的答案。 你可以让它变得更简单,去掉大量的导入。 __init__.py文件的内容
from os import listdir
from os.path import dirname
__all__ = [i[:-3] for i in listdir(dirname(__file__)) if not i.startswith('__') and i.endswith('.py')]

这里有一个解决方案,您不必写文件名。只需将此代码片段添加到__init__.py

from inspect import isclass
from pkgutil import iter_modules
from pathlib import Path
from importlib import import_module


# iterate through the modules in the current package
package_dir = Path(__file__).resolve().parent
for (_, module_name, _) in iter_modules([package_dir]):


# import the module and iterate through its attributes
module = import_module(f"{__name__}.{module_name}")
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)


if isclass(attribute):
# Add the class to this package's variables
globals()[attribute_name] = attribute

Source .