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


269

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

/Foo
    bar.py
    spam.py
    eggs.py

我尝试通过添加__init__.py和执行操作将其转换为程序包,from Foo import *但它没有达到我希望的方式。


2
初始化的.py方式是非常正确的。您可以发布无效的代码吗?
balpha

9
您可以定义“无效”吗?发生了什么?您收到什么错误消息?
S.Lott

是Pythonic还是推荐的? “明确胜于隐含。”
yangmillstheory

显性确实更好。但是,您是否真的想在每次添加新模块时解决这些烦人的“未找到”消息。我认为,如果您的软件包目录包含许多小的模块功能,那么这是解决问题的最佳方法。我的假设是:1.模块相当简单2.您使用该软件包进行代码
整理

Answers:


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文件放到没有进一步配置的目录中,并让它们在其他地方运行的脚本执行。
埃文·佛斯马克

5
@NiallDouglas这个答案是OP提出的一个具体问题,他没有一个zip文件,可以轻松包含pyc文件,而您也忘记了.pyd或.so libs等
Anurag Uniyal 2012年

35
我唯一要添加的就是if not os.path.basename(f).startswith('_')至少if not f.endswith('__init__.py')是列表理解的末尾
Pykler

10
为了使其更强大,还请确保os.path.isfile(f)True。这样可以过滤掉损坏的符号链接和目录,例如somedir.py/(我承认,但仍然...)
MestreLion

12
添加from . import *设置后__all__,如果您希望子模块可使用.(例如,如module.submodule1module.submodule2等)。
ostrokach

133

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

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

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


163
是的,是的,但是有什么办法使其具有动态性?
Evan Fosmark,2009年

27
os.listdir()一些过滤,.py扩展和剥离的组合__all__

不适用于我作为示例代码在github.com/namgivu/python-import-all/blob/master/error_app.py。也许我想念那里的东西?
Nam G VU's

1
我发现自己-要使用这些模块中定义的变量/对象,我们必须使用完整的引用路径,例如moduleName.varNameref。stackoverflow.com/a/710603/248616
Nam G VU

1
@NamGVU:对一个相关问题的回答中的这段代码会将所有公共子模块的名称导入到程序包的命名空间中。
martineau

54

2017年更新:您可能想使用它importlib

通过添加Foo目录使Foo目录成为一个包__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 *并不是一个好主意,原因有几个,其中包括名称冲突以及难以分析代码。


1
不错,但是请不要忘记您也可以导入.pyc和.pyo文件。
埃文·佛斯马克

7
tbh,我觉得__import____all__from . import *
有点黑

1
我认为这比全局版本更好。
lpapp 2014年

8
__import__不为一般用途,它使用的interpreter,使用importlib.import_module()来代替。
Andrew_1510 '16

2
第一个例子非常有帮助,谢谢!在Python的3.6.4我不得不这样做from . import eggs等在__init__.py之前的Python可以导入。尝试进入以上目录中的时,只有import eggs我得到。ModuleNotFoundError: No module named 'eggs'import Foomain.py
尼克,

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也不导入。请参阅下面的automodinit。
尼尔·道格拉斯

1
另一件事:上面的示例未检查sys.modules以查看模块是否已加载。如果没有该检查,则上述内容将第二次加载该模块:)
Niall Douglas 2012年

3
当我用您的代码运行load_all_modules_from_dir('Foo / bar')时,我收到“ RuntimeWarning:处理绝对导入时未找到父模块'Foo / bar'”-要抑制这种情况,我必须设置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

RuntimeWarning还可以通过导入父(AKA目录名)来避免错误(在一个可以说是丑陋的方式)。一种方法是- if dirname not in sys.modules: pkgutil.find_loader(dirname).load_module(dirname)。当然,只有dirname在单组分相对路径下才有效。没有斜线。就个人而言,我更喜欢@Artfunkel的使用基本package_name的方法。
dannysauer

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. 让两个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())
自动修改
#您想要的其他任何东西都可以在这里进行,它不会被修改。

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

for x in __all__: import x

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

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

尼尔


pip无法下载automodinit,因为pypi上没有上传任何内容。
kanzure

感谢您在github上的错误报告。我已在v0.13中修复此问题。Niall
Niall Douglas

11

我知道我正在更新一个很旧的帖子,我尝试使用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 *

然后,将装入包中放置的所有模块,并且如果您编写一个新模块,则该模块也会自动导入。当然,小心翼翼地使用这种东西会带来巨大的责任。


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

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__,则将其视为遇到从包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())

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在哪里定义?
杰普

很抱歉错过了它,现已修复。这是您要导入的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()是为了避免重新导入模块(如果已导入),因为这可能会导致循环导入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.