从子目录中的不同文件导入类

lie*_*ewl 24 python import

这是我正在使用的结构:

directory/
          script.py
          subdir/
                 __init__.py
                 myclass01.py
                 myclass02.py
Run Code Online (Sandbox Code Playgroud)

我想要做的就是在script.py中定义的类进口myclass01.pymyclass02.py.如果我做:

from subdir.myclass01 import *
Run Code Online (Sandbox Code Playgroud)

它适用于定义的类myclass01.py.但是使用这个解决方案,如果在不同的文件中定义了许多类,subdir并且我想要导入所有这些类,我必须为每个文件键入一行.必须有一个捷径.我试过了:

from subdir.* import *
Run Code Online (Sandbox Code Playgroud)

但它没有成功.

编辑:这是文件的内容:

这是__init__.py(使用__all__Apalala建议):

__all__ = ['MyClass01','MyClass02']
Run Code Online (Sandbox Code Playgroud)

这是myclass01.py:

class MyClass01:
    def printsomething():
        print 'hey'
Run Code Online (Sandbox Code Playgroud)

这是myclass02.py:

class MyClass02:
    def printsomething():
        print 'sup'
Run Code Online (Sandbox Code Playgroud)

这是script.py:

from subdir import *
MyClass01().printsomething()
MyClass02().printsomething()
Run Code Online (Sandbox Code Playgroud)

这是我尝试运行时获得的回溯script.py:

File "script.py", line 1, in <module>
    from subdir import *
AttributeError: 'module' object has no attribute 'MyClass01'
Run Code Online (Sandbox Code Playgroud)

mar*_*eau 12

虽然在那里使用的名称与您的问题的目录结构中显示的名称不同,但您可以使用我对题为命名空间和类的问题的答案.在__init__.py那里显示本来还允许usepackage.py脚本已经(被写成这样package映射到subdir你的问题,并Class1myclass01等):

from package import *

print Class1
print Class2
print Class3
Run Code Online (Sandbox Code Playgroud)

修订(更新):

糟糕,抱歉,我的其他答案中的代码并不能完全按照您的意愿行事 - 它只会自动导入任何包子模块的名称.要使它还从每个子模块导入命名属性,还需要几行代码.这是包__init__.py文件的修改版本(也适用于Python 3.4.1):

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__
Run Code Online (Sandbox Code Playgroud)

或者,您可以将上面的内容放在一个单独的.py模块文件中,并在包目录中使用它,__init__.py如下所示:

if __name__ != '__main__':
    from ._import_package_files import *  # defines __all__
    __all__.remove('__all__')  # prevent export (optional)
Run Code Online (Sandbox Code Playgroud)

无论你命名文件是什么,它应该是以_下划线字符开头的东西,所以它不会import递归地尝试自己.

  • 哇,对于这个问题,这是一个相当复杂的解决方案. (7认同)

Apa*_*ala 6

您最好的选择,虽然可能不是最好的样式,但是将所有内容导入到包的命名空间中:

# this is subdir/__init__.py
from myclass01 import *
from myclass02 import *
from myclass03 import *
Run Code Online (Sandbox Code Playgroud)

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

from subdir import Class1
Run Code Online (Sandbox Code Playgroud)


Jus*_*tin 5

我知道自从这个问题得到解答以来已经有几个月了,但是我一直在寻找同样的事情并且浏览了这个页面.我对所选择的答案并不是很满意,所以我最终编写了自己的解决方案,并认为我会分享它.这是我想出的:

# 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
Run Code Online (Sandbox Code Playgroud)

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