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

Eva*_*ark 245 python python-import

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

/Foo
    bar.py
    spam.py
    eggs.py
Run Code Online (Sandbox Code Playgroud)

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

Anu*_*yal 373

列出.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')]
Run Code Online (Sandbox Code Playgroud)

  • 基本上我可以将python文件放到一个没有进一步配置的目录中,并让它们由运行在其他地方的脚本执行. (55认同)
  • 我唯一要补充的是`如果不是os.path.basename(f).startswith('_')`或者至少`if not f.endswith('__ init __.py')`到最后列表理解 (32认同)
  • 添加`来自.如果你希望使用`.`(例如`module.submodule1`,`module.submodule2`等)可以使用子模块,请在设置`__all__`之后导入*`. (10认同)
  • 为了使它更健壮,还要确保`os.path.isfile(f)`是'True`.那将过滤掉破坏的符号链接和目录,如`somedir.py /`(角落,我承认,但仍然......) (9认同)
  • @NiallDouglas这个答案是针对OP提出的具体问题,他没有zip文件,pyc文件可以很容易地包含在内,而你也忘记了.pyd或.so libs等 (5认同)
  • 如果你已经安装了'path.py'(pip install path.py),你可以使用`__all__ = [f.namebase for f in path(__file__).parent.glob('*.py')]`,我觉得看起来更干净。我也同意应该从列表中过滤掉 `__init__.py`。 (2认同)
  • @TomášZato 添加 if 条件以列出理解 (2认同)
  • 这应该是我接受的答案^^ (2认同)

ste*_*anw 127

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

__all__ = ["bar", "spam", "eggs"]
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,是的,但有没有办法让它变得有活力? (157认同)
  • `os.listdir()`的组合,一些过滤,剥离`.py`扩展和`__all__`. (27认同)

Len*_*bro 48

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

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

import bar
import eggs
import spam
Run Code Online (Sandbox Code Playgroud)

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

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

import Foo
Run Code Online (Sandbox Code Playgroud)

您现在可以使用以下方式访问模块

Foo.bar
Foo.eggs
Foo.spam
Run Code Online (Sandbox Code Playgroud)

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

  • tbh,我发现`__import__` hackish,我认为最好把名字添加到`__all__`然后把`放进去.import*`在脚本的底部 (7认同)
  • `__import__`不是一般用途,它由`interpreter`使用,而是使用`importlib.import_module()`. (6认同)
  • 不错,但不要忘记您也可以导入 .pyc 和 .pyo 文件。 (2认同)
  • 我认为这比 glob 版本更好。 (2认同)
  • 第一个例子很有帮助,谢谢!在 Python 3.6.4 下,我必须执行 `from 。在 Python 可以导入之前,在 `__init__.py` 中导入鸡蛋等。仅使用“导入鸡蛋”,我在上面目录中的“main.py”中尝试“导入 Foo”时得到“ModuleNotFoundError: No module named 'eggs'”。 (2认同)
  • 这个答案非常有帮助!但由于某种原因,它在 Py3.5.6 上无法立即运行。我必须将 `.` 附加到 `module` 字符串的开头才能正常工作,否则会出现“没有名为 <filename> 的模块”导入错误。此外,当切换到 `importlib.import_module( )` 时,您有添加参数 `importlib.import_module( modulebase,parent=__name__ )` 使其进入模块的命名空间。如果您想要完整的代码示例,请告诉我。 (2认同)

Luc*_*zzi 40

扩展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')
Run Code Online (Sandbox Code Playgroud)

你会得到:

<module 'Foo.bar' from '/home/.../Foo/bar.pyc'>
<module 'Foo.spam' from '/home/.../Foo/spam.pyc'>
Run Code Online (Sandbox Code Playgroud)

  • 当我用你的代码运行load_all_modules_from_dir('Foo/bar')时,我得到"RuntimeWarning:处理绝对导入时找不到父模块'Foo/bar'" - 为了抑制这个,我必须设置full_package_name ='.'.join( dirname.split(os.path.sep)+ package_name])并导入Foo.bar (3认同)
  • 另一件事:上面的例子没有检查 sys.modules 来查看模块是否已经加载。没有那个检查,上面将第二次加载模块:) (2认同)

Eri*_*ski 24

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

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

  1. 创建一个文件夹/ home/el/foo并main.py在/ home/el/foo下创建一个文件将此代码放在那里:

    from hellokitty import *
    spam.spamfunc()
    ham.hamfunc()
    
    Run Code Online (Sandbox Code Playgroud)
  2. 制作一个目录 /home/el/foo/hellokitty

  3. 使文件__init__.py/home/el/foo/hellokitty,并把这个代码有:

    __all__ = ["spam", "ham"]
    
    Run Code Online (Sandbox Code Playgroud)
  4. 让两个Python文件:spam.pyham.py/home/el/foo/hellokitty

  5. 在spam.py中定义一个函数:

    def spamfunc():
      print "Spammity spam"
    
    Run Code Online (Sandbox Code Playgroud)
  6. 在ham.py中定义一个函数:

    def hamfunc():
      print "Upgrade from baloney"
    
    Run Code Online (Sandbox Code Playgroud)
  7. 运行:

    el@apollo:/home/el/foo$ python main.py 
    spammity spam
    Upgrade from baloney
    
    Run Code Online (Sandbox Code Playgroud)

  • 不仅适合新手,也适合喜欢清晰答案的经验丰富的 Python 开发人员。谢谢。 (12认同)
  • 但使用“import *”被认为是不好的 Python 编码实践。没有那个你怎么做? (3认同)
  • @RachaelBlake,我认为如果将所有函数从一个命名空间导入到全局命名空间中,这被认为是一种不好的做法。因为那里可能会有重叠。在这种情况下,请注意导入文件名的命名空间是必需的(即“spam.spamfunc()”)。`spamfunc` 和 `hamfunc` 不在全局命名空间中,因此不存在重叠的可能性。 (2认同)

Nia*_*las 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
Run Code Online (Sandbox Code Playgroud)

因此,"来自M import*"的效果恰好与"import M"匹配.

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

尼尔


zmo*_*zmo 10

我知道我正在更新一个相当古老的帖子,我尝试使用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__)]))
Run Code Online (Sandbox Code Playgroud)

并在下面的另一个包中yourpackage:

from yourpackage import *
Run Code Online (Sandbox Code Playgroud)

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


Ric*_*cky 6

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


Acu*_*nus 6

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
Run Code Online (Sandbox Code Playgroud)

请注意,这module_name not in globals()是为了避免重新导入已经导入的模块,因为这可能会带来循环导入的风险。


小智 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()
Run Code Online (Sandbox Code Playgroud)

此函数创建一个名为的文件(在提供的文件夹中)__init__.py,该文件包含一个包含__all__文件夹中每个模块的变量.

例如,我有一个名为的文件夹Test ,其中包含:

Foo.py
Bar.py
Run Code Online (Sandbox Code Playgroud)

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

loadImports('Test/')
from Test import *
Run Code Online (Sandbox Code Playgroud)

这将从导入所有Test__init__.py文件Test将现在包含:

__all__ = ['Foo','Bar']
Run Code Online (Sandbox Code Playgroud)


Edu*_*cio 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  
Run Code Online (Sandbox Code Playgroud)


Far*_*eed 5

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

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())
Run Code Online (Sandbox Code Playgroud)


ted*_*ted 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
Run Code Online (Sandbox Code Playgroud)

  • 这会导致一个合法的 mypy 问题:`错误:__all__ 的类型必须是“Sequence[str]”,而不是“List[Module]”`。如果使用基于“import_module”的方法,则不需要定义“__all__”。 (2认同)