如何加载文件夹中的所有模块?

问题描述 投票:220回答:16

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

/Foo
    bar.py
    spam.py
    eggs.py

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

python python-import
16个回答
343
投票

列出当前文件夹中的所有python(.py)文件,并将它们作为__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')]

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 answer有建议的改进!

#!/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__modules - packages医生说

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

...

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

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

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


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

1
投票

查看标准库中的pkgutil模块。只要您在目录中有__init__.py文件,它就会让您完全按照自己的意愿行事。 __init__.py文件可以为空。


1
投票

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

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

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


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

123
投票

__all__变量添加到包含以下内容的__init__.py

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

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


43
投票

更新:今天你可能想用importlib代替。

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

import bar
import eggs
import spam

既然你想要它是动态的(这可能是也可能不是一个好主意),列出所有包含list 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

来自Foo import *的不是一个好主意,原因有几个,包括名称冲突和难以分析代码。


34
投票

扩展Mihail的答案,我相信非hackish方式(如,不直接处理文件路径)如下:

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

20
投票

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

对于那些无法让它工作的新手需要他们的双手。

  1. 创建一个文件夹/ home / el / foo并在/ home / el / foo下创建一个文件main.py将此代码放在那里: from hellokitty import * spam.spamfunc() ham.hamfunc()
  2. 创建一个目录/home/el/foo/hellokitty
  3. __init__.py下创建一个文件/home/el/foo/hellokitty并将此代码放在那里: __all__ = ["spam", "ham"]
  4. 制作两个python文件:spam.py下的ham.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

16
投票

我自己厌倦了这个问题,所以我写了一个名为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

因此,“来自M import *”的效果恰好与“import M”匹配。

automodinit很高兴从ZIP档案中运行,因此ZIP安全。

尼尔


8
投票

我知道我正在更新一个相当古老的帖子,我尝试使用automodinit,但发现它的设置过程因python3而中断。所以,根据Luca的回答,我提出了一个更简单的答案 - 这可能不适用于.zip - 这个问题,所以我想我应该在这里分享它:

__init__.pyyourpackage模块中:

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

yourpackage下面的另一个包中:

from yourpackage import *

然后你将加载包中的所有模块,如果你编写一个新模块,它也将自动导入。当然,谨慎使用这种东西,拥有强大的力量是很重要的。


5
投票
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']
© www.soinside.com 2019 - 2024. All rights reserved.