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

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

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

/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.


回答 0

列出.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')]

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

回答 1

__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


回答 2

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 *并不是一个好主意,原因有几个,其中包括名称冲突以及难以分析代码。

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.


回答 3

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

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

回答 4

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

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
    

回答 5

我自己对这个问题感到厌倦,所以写了一个叫做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安全的。

尼尔

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


回答 6

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


回答 7

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)

回答 8

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

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

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

回答 9

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

回答 10

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  

回答 11

看到您的__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 *将导入声音包的三个命名子模块。

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.


回答 12

这是到目前为止我发现的最好方法:

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

回答 13

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

回答 14

查看标准库中的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.


回答 15

我为此创建了一个模块,该模块不依赖__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


回答 16

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

回答 17

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()是为了避免重新导入模块(如果已导入),因为这可能会导致循环导入。

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.