从多个目录导入python模块

时间:2013-06-26 05:39:38

标签: python

我有一个python 2.6 Django应用程序,它有一个像这样的文件夹结构:

/foo/bar/__init__.py

我在文件系统上有另外几个目录,里面装满了这样的python模块:

/modules/__init__.py
/modules/module1/__init__.py
/other_modules/module2/__init__.py
/other_modules/module2/file.py

每个模块__init__都有一个类。例如分别为module1Class()module2Class()。在module2中,file.py包含一个名为myFileClass()的类。

我想做的是在/foo/bar/__init__.py中添加一些代码,以便我可以像这样导入我的Django项目:

from foo.bar.module1 import module1Class
from foo.bar.module2 import module2Class
from foo.bar.module2.file import myFileClass

具有模块的目录列表包含在Django配置中的元组中,如下所示:

module_list = ("/modules", "/other_modules",)

我尝试使用__import__vars()动态生成如下变量:

import os
import sys 

for m in module_list:
    sys.path.insert(0, m)
    for d in os.listdir(m):
        if os.path.isdir(d):
            vars()[d] = getattr(__import__(m.split("/")[-1], fromlist=[d], d)

但这似乎不起作用。有没有办法做到这一点?

谢谢!

2 个答案:

答案 0 :(得分:1)

我可以看到您的代码至少存在一个问题。这条线...

if os.path.isdir(d):

...无效,因为os.listdir()会返回相对路径名,因此您需要将它们转换为绝对路径名,否则os.path.isdir()将返回False因为路径不存在(相对于当前工作目录),而不是引发异常(这会更有意义,IMO)。

以下代码适用于我......

import sys
import os

# Directories to search for packages
root_path_list = ("/modules", "/other_modules",)

# Make a backup of sys.path
old_sys_path = sys.path[:]

# Add all paths to sys.path first, in case one package imports from another
for root_path in root_path_list:
    sys.path.insert(0, root_path)

# Add new packages to current scope
for root_path in root_path_list:
    filenames = os.listdir(root_path)
    for filename in filenames:
        full_path = os.path.join(root_path, filename)
        if os.path.isdir(full_path):
            locals()[filename] = __import__(filename)

# Restore sys.path
sys.path[:] = old_sys_path

# Clean up locals
del sys, os, root_path_list, old_sys_path, root_path, filenames, filename, full_path

<强>更新

考虑到这一点,检查是否存在__init__.py可能更安全,而不是使用os.path.isdir()以防您有不包含此类文件的子目录,否则{{1将失败。

所以你可以改变线......

__import__()

...到...

        full_path = os.path.join(root_path, filename)
        if os.path.isdir(full_path):
            locals()[filename] = __import__(filename)

......但可能没必要。

答案 1 :(得分:0)

我们咬紧牙关,改变了我们做事的方式。现在,在Django配置中传递查找模块的目录列表,并将每个目录添加到sys.path中(类似于Aya提到的评论以及之前我做过但不太满意的事情)。然后对于其中的每个模块,我们检查__init__.py,如果它存在,尝试将其视为在应用程序内部使用的模块,而不使用foo.bar块。

这需要对我们如何与模块进行交互以及开发人员如何编写模块进行一些调整(他们现在需要在模块中使用相对导入而不是之前使用的完整路径导入)但我认为这将是一个更容易的设计供开发人员长期使用。

我们没有将这些添加到INSTALLED_APPS,因为我们做了一些异常处理,如果由于依赖性问题或错误的代码我们无法导入模块,我们的软件将在没有该模块的情况下继续运行。如果它们处于INSTALLED_APPS状态,我们将无法在何时/如何处理这些异常时利用这种灵活性。

感谢您的帮助!