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

发布于 2024-07-26 02:59:57 字数 212 浏览 7 评论 0原文

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

/Foo
    bar.py
    spam.py
    eggs.py

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

Could someone provide me with a good way of importing a whole directory of modules?
I have a structure like this:

/Foo
    bar.py
    spam.py
    eggs.py

I tried just converting it to a package by adding __init__.py and doing from Foo import * but it didn't work the way I had hoped.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

£烟消云散 2024-08-02 02:59:58

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

When from . import * isn't good enough, this is an improvement over the answer by ted. Specifically, the use of __all__ is not necessary with this approach.

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

Note that module_name not in globals() is intended to avoid reimporting the module if it's already imported, as this can risk cyclic imports.

山川志 2024-08-02 02:59:58

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

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

This is the best way i've found so far:

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())
金兰素衣 2024-08-02 02:59:58

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  

Anurag Uniyal answer with suggested improvements!

#!/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  
涫野音 2024-08-02 02:59:58

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

Using importlib the only thing you've got to add is

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
水水月牙 2024-08-02 02:59:58

我想添加 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')]

I'd like to add to Anurag Uniyal's answer.
You can make it even simpler and get rid of a lot of the imports.
Contents of the __init__.py file:

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')]
ゃ懵逼小萝莉 2024-08-02 02:59:58

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

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

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

I've created a module for that, which doesn't rely on __init__.py (or any other auxiliary file) and makes me type only the following two lines:

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

Feel free to re-use or contribute: http://gitlab.com/aurelien-lourot/importdir

谜泪 2024-08-02 02:59:58

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

Anurag's example with a couple of corrections:

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")]
全部不再 2024-08-02 02:59:58

查看您的 __init__.py 定义了 __all__模块 - 包 文档说

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

...

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

__all__ = ["回声", "环绕", "反向"]

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

See that your __init__.py defines __all__. The modules - packages doc says

The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

...

The only solution is for the package author to provide an explicit index of the package. The import statement uses the following convention: if a package’s __init__.py code defines a list named __all__, it is taken to be the list of module names that should be imported when from package import * is encountered. It is up to the package author to keep this list up-to-date when a new version of the package is released. Package authors may also decide not to support it, if they don’t see a use for importing * from their package. For example, the file sounds/effects/__init__.py could contain the following code:

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

This would mean that from sound.effects import * would import the three named submodules of the sound package.

内心荒芜 2024-08-02 02:59:58

查看标准库中的 pkgutil 模块。 只要目录中有 __init__.py 文件,它就可以让您完全执行您想要的操作。 __init__.py 文件可以为空。

Look at the pkgutil module from the standard library. It will let you do exactly what you want as long as you have an __init__.py file in the directory. The __init__.py file can be empty.

无法言说的痛 2024-08-02 02:59:58

这是一个解决方案,使用它您不必写文件名。 只需将此代码片段添加到您的 __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

Here is a solution, with which you do not have to write the file name. Just add this code snippet to your __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

雅心素梦 2024-08-02 02:59:58

在 Python 3.9.5、Flask 2.2.2 中,没有一个解决方案对我有用,该模块是 cwd 下 2 级的目录。
这是我的解决方案:

import importlib
import pathlib
import re

path = pathlib.Path(__file__).parent.absolute()
names = [x.name[:-3] for x in path.iterdir() if x.is_file() and re.search("^[a-z]*\.py$", x.name)]
for name in names:
    importlib.import_module(f".{name}", __name__)

None of the solutions was working for me in Python 3.9.5, Flask 2.2.2, the module being a directory 2 levels down the cwd.
This is my solution:

import importlib
import pathlib
import re

path = pathlib.Path(__file__).parent.absolute()
names = [x.name[:-3] for x in path.iterdir() if x.is_file() and re.search("^[a-z]*\.py
quot;, x.name)]
for name in names:
    importlib.import_module(f".{name}", __name__)
究竟谁懂我的在乎 2024-08-02 02:59:58

只需通过 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

Just import them by importlib and add them to __all__ (add action is optional) in recurse in the __init__.py of package.

/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
无畏 2024-08-02 02:59:58

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

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

I had a nested directory structure i.e. I had multiple directories inside the main directory that contained the python modules.

I added the following script to my __init__.py file to import all the modules

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
quot;, module_path):
        import_path = module_path[:-3]
        import_path = import_path.replace("/", ".")
        exec(f"from .{import_path} import *")

os.chdir(owd)

Probably not the best way to achieve this, but I couldn't make anything else work for me.

暮年 2024-08-02 02:59:57

列出当前文件夹中的所有 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')]

List all python (.py) files in the current folder and put them as __all__ variable in __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')]
一枫情书 2024-08-02 02:59:57

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

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

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

Add the __all__ Variable to __init__.py containing:

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

See also http://docs.python.org/tutorial/modules.html

陌路终见情 2024-08-02 02:59:57

2017 年更新:您可能想使用 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

您现在可以使用 etc 访问模块

Foo.bar
Foo.eggs
Foo.spam

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

Update in 2017: you probably want to use importlib instead.

Make the Foo directory a package by adding an __init__.py. In that __init__.py add:

import bar
import eggs
import spam

Since you want it dynamic (which may or may not be a good idea), list all py-files with list dir and import them with something like this:

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

Then, from your code do this:

import Foo

You can now access the modules with

Foo.bar
Foo.eggs
Foo.spam

etc. from Foo import * is not a good idea for several reasons, including name clashes and making it hard to analyze the code.

夜巴黎 2024-08-02 02:59:57

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

对于那些无法让它工作、需要双手握住的新手。

  1. 创建一个文件夹 /home/el/foo 并在 /home/el/foo 下创建一个文件 main.py 将以下代码放入其中:

    来自hellokitty导入* 
      spam.spamfunc() 
      ham.hamfunc() 
      
  2. 创建一个目录/home/el/foo/hellokitty

  3. 创建一个文件 __init__.py< /code> 下 /home/el/foo/hellokitty 并将此代码放在那里:

    __all__ = ["垃圾邮件", "火腿"] 
      

  4. 制作两个 python文件:/home/el/foo/hellokitty

    下的 spam.pyham.py

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

    def spamfunc(): 
        print("垃圾邮件") 
      
  6. 在 ham.py 中定义一个函数:

    def hamfunc(): 
        print("从胡说八道升级") 
      
  7. 运行它:

    el@apollo:/home/el/foo$ python main.py  
      垃圾邮件 
      从胡言乱语升级 
      

Python, include all files under a directory:

For newbies who just can't get it to work who need their hands held.

  1. Make a folder /home/el/foo and make a file main.py under /home/el/foo Put this code in there:

    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
    
伪装你 2024-08-02 02:59:57

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

Expanding on Mihail's answer, I believe the non-hackish way (as in, not handling the file paths directly) is the following:

  1. create an empty __init__.py file under Foo/
  2. Execute
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')

You'll get:

<module 'Foo.bar' from '/home/.../Foo/bar.pyc'>
<module 'Foo.spam' from '/home/.../Foo/spam.pyc'>
紫罗兰の梦幻 2024-08-02 02:59:57

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

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

I know I'm updating a quite old post, and I tried using automodinit, but found out it's setup process is broken for python3. So, based on Luca's answer, I came up with a simpler answer - which might not work with .zip - to this issue, so I figured I should share it here:

within the __init__.py module from yourpackage:

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

and within another package below yourpackage:

from yourpackage import *

Then you'll have all the modules that are placed within the package loaded, and if you write a new module, it'll be automagically imported as well. Of course, use that kind of things with care, with great powers comes great responsibilities.

开始看清了 2024-08-02 02:59:57

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

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

automodinit 很乐意从 ZIP 存档内部运行,因此是 ZIP 安全的。

尼尔

I got tired of this problem myself, so I wrote a package called automodinit to fix it. You can get it from http://pypi.python.org/pypi/automodinit/.

Usage is like this:

  1. Include the automodinit package into your setup.py dependencies.
  2. Replace all __init__.py files like this:
__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.

That's it! From now on importing a module will set __all__ to
a list of .py[co] files in the module and will also import each
of those files as though you had typed:

for x in __all__: import x

Therefore the effect of "from M import *" matches exactly "import M".

automodinit is happy running from inside ZIP archives and is therefore ZIP safe.

Niall

夜司空 2024-08-02 02:59:57
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
  __import__(module)
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
  __import__(module)
终陌 2024-08-02 02:59:57

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

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 *

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

__all__ = ['Foo','Bar']

I have also encountered this problem and this was my solution:

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

This function creates a file (in the provided folder) named __init__.py, which contains an __all__ variable that holds every module in the folder.

For example, I have a folder named Test
which contains:

Foo.py
Bar.py

So in the script I want the modules to be imported into I will write:

loadImports('Test/')
from Test import *

This will import everything from Test and the __init__.py file in Test will now contain:

__all__ = ['Foo','Bar']
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文