关于python:从子目录中的不同文件导入类

Importing classes from different files in a subdirectory

以下是我正在使用的结构:

1
2
3
4
5
6
directory/
          script.py
          subdir/
                 __init__.py
                 myclass01.py
                 myclass02.py

我要做的是在script.py中导入在myclass01.pymyclass02.py中定义的类。如果我这样做:

1
from subdir.myclass01 import *

对于myclass01.py中定义的类,它可以正常工作。但是使用这个解决方案,如果在subdir的不同文件中定义了许多类,并且我想导入所有类,那么我必须为每个文件键入一行。必须有一个快捷方式。我试过:

1
from subdir.* import *

但没有成功。

编辑:以下是文件的内容:

这是__init__.py(根据apalala的建议,使用__all__):

1
__all__ = ['MyClass01','MyClass02']

我是myclass01.py

1
2
3
class MyClass01:
    def printsomething():
        print 'hey'

我是myclass02.py

1
2
3
class MyClass02:
    def printsomething():
        print 'sup'

我是script.py

1
2
3
from subdir import *
MyClass01().printsomething()
MyClass02().printsomething()

这是我在运行script.py时得到的回溯:

1
2
3
File"script.py", line 1, in <module>
    from subdir import *
AttributeError: 'module' object has no attribute 'MyClass01'


尽管此处使用的名称与问题目录结构中显示的名称不同,但您可以使用我对题为"名称间距和类"的问题的回答。这里显示的__init__.py也允许以这种方式编写usepackage.py脚本(在您的问题中,package映射到subdirClass1映射到myclass01等):

1
2
3
4
5
from package import *

print Class1
print Class2
print Class3

修订(更新):

对不起,我的另一个答案中的代码并不能完全满足您的需要——它只会自动导入任何包子模块的名称。要使其同时从每个子模块导入命名属性,还需要几行代码。下面是该包的__init__.py文件的修改版本(也适用于python 3.4.1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def _import_package_files():
   """ Dynamically import all the public attributes of the python modules in this
        file's directory (the package directory) and return a list of their names.
   """

    import os
    exports = []
    globals_, locals_ = globals(), locals()
    package_path = os.path.dirname(__file__)
    package_name = os.path.basename(package_path)

    for filename in os.listdir(package_path):
        modulename, ext = os.path.splitext(filename)
        if modulename[0] != '_' and ext in ('.py', '.pyw'):
            subpackage = '{}.{}'.format(package_name, modulename) # pkg relative
            module = __import__(subpackage, globals_, locals_, [modulename])
            modict = module.__dict__
            names = (modict['__all__'] if '__all__' in modict else
                     [name for name in modict if name[0] != '_'])  # all public
            exports.extend(names)
            globals_.update((name, modict[name]) for name in names)

    return exports

if __name__ != '__main__':
    __all__ = ['__all__'] + _import_package_files()  # '__all__' in __all__

或者,您可以将上述内容放入包目录中自己的单独.py模块文件中,并从包的__init__.py中使用它,如下所示:

1
2
3
if __name__ != '__main__':
    from ._import_package_files import *  # defines __all__
    __all__.remove('__all__')  # prevent export (optional)

无论您如何命名该文件,它都应该是以_下划线字符开头的内容,这样它就不会尝试递归地对import本身进行命名。


您最好的选择是将所有内容导入包的命名空间,尽管这可能不是最佳样式:

1
2
3
4
# this is subdir/__init__.py
from myclass01 import *
from myclass02 import *
from myclass03 import *

然后,在其他模块中,可以直接从包中导入所需内容:

1
from subdir import Class1


我知道这个问题被回答已经有几个月了,但我在找同样的东西,并浏览了这一页。我对选择的答案不太满意,所以最后我写了自己的解决方案,并想和大家分享一下。我想到的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# NOTE: The function name starts with an underscore so it doesn't get deleted by iself
def _load_modules(attr_filter=None):
    import os

    curdir = os.path.dirname(__file__)
    imports = [os.path.splitext(fname)[0] for fname in os.listdir(curdir) if fname.endswith(".py")]

    pubattrs = {}
    for mod_name in imports:
        mod = __import__(mod_name, globals(), locals(), ['*'], -1)

        for attr in mod.__dict__:
            if not attr.startswith('_') and (not attr_filter or attr_filter(mod_name, attr)):
                pubattrs[attr] = getattr(mod, attr)

    # Restore the global namespace to it's initial state
    for var in globals().copy():
        if not var.startswith('_'):
            del globals()[var]

    # Update the global namespace with the specific items we want
    globals().update(pubattrs)

# EXAMPLE: Only load classes that end with"Resource"
_load_modules(attr_filter=lambda mod, attr: True if attr.endswith("Resource") else False)
del _load_modules # Keep the namespace clean

这只需从包目录中的所有.py文件导入*,然后只将公共文件拉入全局命名空间。此外,如果只需要某些公共属性,它还允许使用过滤器。


我用这个简单的方法:

  • 将目录添加到系统路径,然后
  • 该目录中的import modulefrom module import function1, class1
  • 注意,module只是您的*.py文件的名称,没有扩展名部分。

    下面是一个一般示例:

    1
    2
    3
    import sys
    sys.path.append("/path/to/folder/")
    import module # in that folder

    在您的案例中,可能是这样的:

    1
    2
    3
    4
    5
    import sys
    sys.path.append("subdir/")
    import myclass01
    # or
    from myclass01 import func1, class1, class2 # .. etc

    1
    from subdir.* import *

    不能在"From"语句后直接使用"*"。你需要解释导入。请查看有关导入和包的python文档。