319

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

/Foo
    bar.py
    spam.py
    eggs.py

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

4

20 回答 20

454

列出当前文件夹中的所有 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')]
于 2009-06-29T10:06:25.593 回答
147

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

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

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

于 2009-06-29T09:42:33.830 回答
69

2017 年更新:您可能想importlib改用。

通过添加__init__.py. 在那个__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

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

于 2009-06-29T11:10:40.363 回答
43

扩展 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'>
于 2011-12-19T02:11:12.577 回答
42

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. 制作两个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
    
于 2013-12-24T00:08:56.313 回答
16

我自己也厌倦了这个问题,所以我写了一个名为 automodinit 的包来修复它。您可以从http://pypi.python.org/pypi/automodinit/获取它。

用法是这样的:

  1. automodinit将包包含到您的setup.py依赖项中。
  2. 像这样替换所有 __init__.py 文件:
__all__ = [“我将被重写”]
# 不要修改上面的那行,或者这行!
导入 automodinit
automodinit.automodinit(__name__, __file__, globals())
删除自动修改
# 你想要的任何东西都可以在这里,它不会被修改。

就是这样!从现在开始,导入模块会将 __all__ 设置为模块中的 .py[co] 文件列表,并且还将导入每个文件,就像您输入的一样:

for x in __all__: import x

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

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

尼尔

于 2012-03-05T02:24:09.297 回答
16

我知道我正在更新一篇相当旧的帖子,我尝试使用automodinit,但发现它的设置过程对于 python3 来说是错误的。所以,根据 Luca 的回答,我想出了一个更简单的答案——它可能不适用于 .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 *

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

于 2016-03-26T02:54:58.540 回答
8
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
  __import__(module)
于 2016-06-07T16:15:03.237 回答
6

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

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']
于 2012-07-30T21:06:25.390 回答
5

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  
于 2014-06-13T01:28:24.067 回答
5

使用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
于 2019-11-26T15:56:51.090 回答
4

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

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())
于 2014-07-02T08:30:27.257 回答
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")]
于 2014-11-19T11:13:09.087 回答
4

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

于 2020-03-26T04:27:11.267 回答
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 *将导入声音包的三个命名子模块。

于 2009-06-29T09:47:46.800 回答
3

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

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

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

于 2014-11-18T23:46:56.803 回答
2

我想补充一下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')]
于 2021-07-18T15:44:30.673 回答
1

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

于 2011-02-08T12:51:32.667 回答
0

只需通过importlib将它们导入并在of 包中以递归方式将它们添加到__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
于 2018-01-18T11:07:46.670 回答
-1

我有一个嵌套的目录结构,即我在包含 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)

可能不是实现这一目标的最佳方式,但我无法让其他任何东西为我工作。

于 2021-01-15T21:41:12.180 回答