0

我有一个 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)

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

谢谢!

4

2 回答 2

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()在您有不包含此类文件的子目录的情况下使用,否则__import__()将失败。

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

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

...到...

        full_path = os.path.join(root_path, filename, '__init__.py')
        if os.path.exists(full_path):
            locals()[filename] = __import__(filename)

...但这可能是不必要的。

于 2013-06-26T14:38:24.833 回答
0

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

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

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

感谢所有的帮助!

于 2013-07-05T01:45:34.673 回答