【问题标题】:How to import members of all modules within a package?如何导入包中所有模块的成员?
【发布时间】:2013-01-03 19:05:35
【问题描述】:

我正在开发一个文件结构类似于以下的包:

test.py
package/
    __init__.py
    foo_module.py
    example_module.py

如果我在 test.py 中调用 import package,我希望包模块看起来像这样:

>>> vars(package)
mapping_proxy({foo: <function foo at 0x…}, {example: <function example at 0x…})

换句话说,我希望package 中的所有模块的成员都在package 的命名空间中,并且我不希望模块本身在命名空间中。 package 不是子包。

假设我的文件如下所示:

foo_module.py:

def foo(bar):
    return bar

example_module.py:

def example(arg):
    return foo(arg)

test.py:

print(example('derp'))

如何构建 test.py、example_module.py 和 __init__.py 中的导入语句,以便在包目录之外(即 test.py)和包本身(即 foo_module.py 和 example_module.py )?我尝试的所有东西都会给Parent module '' not loaded, cannot perform relative importImportError: No module named 'module_name'

另外,作为旁注(根据 PEP 8):“非常不鼓励对包内导入进行相对导入。始终对所有导入使用绝对包路径。即使现在 PEP 328 已在 Python 2.5 中完全实现,它的显式相对导入风格被积极劝阻;绝对导入更便携,通常更具可读性。”

我正在使用 Python 3.3。

【问题讨论】:

  • 我只是盲目地放在这里,没有尝试:from package import *怎么样?

标签: python import python-3.x package


【解决方案1】:

我希望包中所有模块的成员都在包中 命名空间,我不希望模块本身位于 命名空间。

我能够通过调整我在 Python 2 中使用的东西来自动导入插件以在 Python 3 中也能工作来做到这一点。

简而言之,它的工作原理如下:

  1. 包的__init__.py 文件导入同一包目录中的所有其他Python 文件,但名称以'_'(下划线)字符开头的文件除外。

  2. 然后它将导入模块的名称空间中的任何名称添加到__init__ 模块的名称(也是包的名称空间)。请注意,我必须从 .foo_module 明确地创建 example_module 模块 import foo

以这种方式做事的一个重要方面是意识到它是动态的并且不需要将包模块名称硬编码到__init__.py 文件中。当然,这需要更多的代码来完成,但也使它非常通用并且能够与几乎任何(单级)包一起使用——因为它会在添加新模块时自动导入新模块,并且不再尝试导入任何已删除的模块从目录中。

test.py

from package import *

print(example('derp'))

__init__.py

def _import_all_modules():
    """ Dynamically imports all modules in this package. """
    import traceback
    import os
    global __all__
    __all__ = []
    globals_, locals_ = globals(), locals()

    # Dynamically import all the package modules in this file's directory.
    for filename in os.listdir(__name__):
        # Process all python files in directory that don't start
        # with underscore (which also prevents this module from
        # importing itself).
        if filename[0] != '_' and filename.split('.')[-1] in ('py', 'pyw'):
            modulename = filename.split('.')[0]  # Filename sans extension.
            package_module = '.'.join([__name__, modulename])
            try:
                module = __import__(package_module, globals_, locals_, [modulename])
            except:
                traceback.print_exc()
                raise
            for name in module.__dict__:
                if not name.startswith('_'):
                    globals_[name] = module.__dict__[name]
                    __all__.append(name)

_import_all_modules()

foo_module.py

def foo(bar):
    return bar

example_module.py

from .foo_module import foo  # added

def example(arg):
    return foo(arg)

【讨论】:

    【解决方案2】:

    我认为通过使用from module import name 样式导入,您可以获得所需的值而不会弄乱您的命名空间。我认为这些导入将满足您的要求:

    example_module.py 的导入:

    from package.foo_module import foo
    

    __init__.py 导入:

    from package.foo_module import foo
    from package.example_module import example
    
    __all__ = [foo, example] # not strictly necessary, but makes clear what is public
    

    test.py 导入:

    from package import example
    

    请注意,这仅在您运行test.py(或包层次结构的同一级别的其他内容)时才有效。否则,您需要确保包含 package 的文件夹位于 python 模块搜索路径中(通过将包安装到 Python 会查找它的位置,或者将适当的文件夹添加到 sys.path)。

    【讨论】:

    • +1:例如,multiprocessing package from stdlib 使用这种技术
    • 如果我直接执行 example_module.py 或 __init__.py,我得到一个导入错误,但 test.py 有效。获取 test.py 显然是最重要的开始工作,但是有没有办法(没有 try/catch)让另外两个也能工作?我想这就是我首先发布这个问题的原因。
    • 这也将模块保留在包的命名空间中。现在想来,这是Python的要求吗?
    • @TylerCrompton:一般来说,不,没有很好的方法可以从包中将模块作为脚本运行。可以将sys.path.append("..") 之类的内容放在example_module.py 中,但这是一种技巧,可能无法完美运行(您也可以尝试-m 命令行选项)。 Python 开发人员几乎说,在包中运行脚本从根本上来说是一种破坏设计,并且他们不打算永远支持它(尽管新的 Python 程序员经常要求它)。
    • 我不确定我是否理解您关于包命名空间的问题。如果您输入import package.foo_module,您确实会得到foo_module 模块,但如果您只是import package,则package.foo_module 不会自动出现任何内容(仅在package.foopackage.bar)。即使from package import *(通常不鼓励这样做)也应该做正确的事。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-18
    • 2011-03-22
    • 2015-06-01
    • 1970-01-01
    相关资源
    最近更新 更多