简体   繁体   English

从多个目录导入python模块

[英]Importing python modules from multiple directories

I have a python 2.6 Django app which has a folder structure like this: 我有一个python 2.6 Django应用程序,它的文件夹结构如下:

/foo/bar/__init__.py

I have another couple directories on the filesystem full of python modules like this: 我在文件系统上还有另外几个目录,其中充满了python模块,如下所示:

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

Each module __init__ has a class. 每个模块__init__都有一个类。 For example module1Class() and module2Class() respectively. 例如分别为module1Class()module2Class() In module2 , file.py contains a class called myFileClass() . module2file.py包含一个名为myFileClass()的类。

What I would like to do is put some code in /foo/bar/__init__.py so I can import in my Django project like this: 我想做的是在/foo/bar/__init__.py放入一些代码,这样我就可以像这样导入Django项目:

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

The list of directories which have modules is contained in a tuple in a Django config which looks like this: 具有模块的目录列表包含在Django配置的元组中,如下所示:

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

I've tried using __import__ and vars() to dynamically generate variables like this: 我试过使用__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)

But that doesn't seem to work. 但这似乎不起作用。 Is there any way to do this? 有什么办法吗?

Thanks! 谢谢!

I can see at least one problem with your code. 我可以看到您的代码中至少有一个问题。 The line... 线...

if os.path.isdir(d):

...won't work, because os.listdir() returns relative pathnames, so you'll need to convert them to absolute pathnames, otherwise the os.path.isdir() will return False because the path doesn't exist (relative to the current working directory), rather than raising an exception (which would make more sense, IMO). ...不起作用,因为os.listdir()返回相对路径名,所以您需要将它们转换为绝对路径名,否则os.path.isdir()将返回False因为该路径不存在(相对于当前工作目录),而不是引发异常(IMO会更有意义)。

The following code works for me... 以下代码对我有用...

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

Update 更新

Thinking about it, it might be safer to check for the presence of __init__.py , rather than using os.path.isdir() in case you have subdirectories which don't contain such a file, otherwise the __import__() will fail. 考虑一下,如果子目录中不包含此类文件,则检查__init__.py的存在可能比使用os.path.isdir()更安全,否则__import__()将会失败。

So you could change the lines... 所以你可以改变线...

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

...to... ...至...

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

...but it might be unnecessary. ...但是可能没有必要。

We wound up biting the bullet and changing how we do things. 我们全力以赴,改变了工作方式。 Now the list of directories to find modules is passed in the Django config and each one is added to sys.path (similar to a comment Aya mentioned and something I did before but wasn't too happy with). 现在,用于查找模块的目录列表在Django配置中传递,并且每个目录都添加到sys.path中(类似于Aya提到的注释以及我之前做过但不太满意的操作)。 Then for each module inside of it, we check for an __init__.py and if it exists, attempt to treat it as a module to use inside of the app without using the foo.bar piece. 然后,针对其中的每个模块,检查__init__.py ,如果存在,则尝试将其视为要在应用程序内部使用的模块,而不使用foo.bar

This required some adjustment on how we interact with the modules and how developers code their modules (they now need to use relative imports within their module instead of the full path imports they used before) but I think this will be an easier design for developers to use long-term. 这需要对我们与模块的交互方式以及开发人员对模块的编码方式进行一些调整(现在他们需要在模块中使用相对导入,而不是之前使用的完整路径导入),但是我认为这对于开发人员而言将是一个更容易的设计长期使用。

We didn't add these to INSTALLED_APPS because we do some exception handling where if we cannot import a module due to dependency issues or bad code our software will continue running just without that module. 我们没有将它们添加到INSTALLED_APPS中,因为我们进行了一些异常处理,如果由于依赖关系问题或错误的代码而无法导入模块,则我们的软件将在没有该模块的情况下继续运行。 If they were in INSTALLED_APPS we wouldn't be able to leverage that flexibility on when/how to deal with those exceptions. 如果它们在INSTALLED_APPS中,则我们将无法在何时/如何处理这些异常上利用这种灵活性。

Thanks for all of the help! 感谢您的所有帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM