【问题标题】:How to reference to the top-level module in Python inside a package?如何在包内引用 Python 中的顶级模块?
【发布时间】:2012-02-01 03:23:04
【问题描述】:

在下面的层次结构中,是否有一种方便且通用的方式来使用下面所有 .py 文件中的通用术语来引用 top_package?我希望有一种一致的方式来导入其他模块,这样即使“top_package”更改名称也不会中断。

我不赞成使用像“..level_one_a”这样的相对导入,因为相对路径将与下面的每个 python 文件不同。我正在寻找一种方法:

  1. 对于包中的同一个模块,每个 python 文件可以有相同的导入语句。
  2. 在包内的任何 .py 文件中对“top_package”的解耦引用,因此无论“top_package”名称更改为什么,都不会中断。

    top_package/
      __init__.py
      level_one_a/
        __init__.py
        my_lib.py
        level_two/
          __init__.py
          hello_world.py
      level_one_b/
        __init__.py
        my_lib.py
      main.py
    

【问题讨论】:

  • 为什么要引用顶层模块? (有时这个答案揭示了真正的问题/解决方案。)
  • 我想让包尽可能地复用,这样当我重命名顶层模块时,一切仍然可以正常工作,而不必更改里面的每个导入名称。

标签: python module packages


【解决方案1】:

这应该可以完成工作:

top_package = __import__(__name__.split('.')[0])

这里的诀窍是,对于每个模块,__name__ 变量包含模块的完整路径,用点分隔,例如top_package.level_one_a.my_lib。因此,如果您想获取顶级包名,您只需要获取路径的第一个组件并使用__import__ 导入即可。

尽管用于访问包的变量名称仍称为top_package,但您可以重命名包,如果仍然可以使用。

【讨论】:

  • -1 该问题要求一个不依赖于名称“top_package”的解决方案。
  • @Jon-Eric 感谢您的评论。无论顶部包名称如何,我都已修复我的答案。
  • 这并不完全正确;如果当前模块不是从顶层导入的,则其__name__ 将不包含该模块的完整路径,而仅包含其中的一部分,它将从您导入该模块的位置开始。
【解决方案2】:

我相信如果不使用相对导入或命名包,#2 是不可能的。您必须通过显式调用其名称或使用相对导入来指定要导入的模块。否则口译员怎么知道你想要什么?

如果您将应用程序启动器设置为比top_level/ 高一级并拥有import top_level,那么您可以从top_level 包内的任何位置引用top_level.*

(我可以向您展示我正在开发的软件的示例:http://github.com/toddself/beerlog/

【讨论】:

    【解决方案3】:

    您可以结合使用 __import__() 函数和包的 __path__ 属性。

    例如,假设您希望从包中的其他位置导入<whatever>.level_one_a.level_two.hello_world。你可以这样做:

    import os
    _temp = __import__(__path__[0].split(os.sep)[0] + ".level_one_a.level_two.hello_world")
    my_hello_world = _temp.level_one_a.level_two.hello_world
    

    此代码独立于顶级包的名称,可以在包中的任何位置使用。也很丑。

    【讨论】:

      【解决方案4】:

      将您的包和main 脚本放入外部容器目录中,如下所示:

      container/
          main.py
          top_package/
              __init__.py
              level_one_a/
                  __init__.py
                  my_lib.py
                  level_two/
                      __init__.py
                      hello_world.py
              level_one_b/
                  __init__.py
                  my_lib.py
      

      main.py 运行时,其父目录(container)会自动添加到sys.path 的开头。由于top_package 现在位于同一目录中,因此可以从包树中的任何位置导入它。

      所以hello_world.py 可以像这样导入level_one_b/my_lib.py

      from top_package.level_one_b import my_lib
      

      无论容器目录的名称是什么,或者它位于何处,导入将始终使用这种安排。

      但请注意,在您的原始示例中,top_package 它可以很容易地用作容器目录本身。您所要做的就是删除top_package/__init__.py,您将得到有效的相同安排。

      之前的导入语句将更改为:

      from level_one_b import my_lib
      

      您可以随意重命名top_package

      【讨论】:

      • 如果你去掉top_package/__init__.py,那么level_one_a和level_one_b就不能互相导入了。
      • @DonGar。不——它会工作得很好。但请注意,我指的是 OPs 问题中的结构,而不是我的答案中显示的结构。关键区别在于main.py 脚本的位置。
      • 我同意您回答的主要部分,但以“但请注意”开头的段落是我不同意的。除非您还意味着将 level_one_a 和 level_one_b 都移动到 top_container 的新子模块中。
      • @DonGar,你显然没有用真实的代码做任何测试。我的答案中的所有内容都完全按照说明进行。我不明白你的分歧是基于什么。
      • 是的,我有。事实上,我只是经历了第一次设置不使用路径修改的项目的痛苦过程。但是,经过反思,如果由 main 导入,库可以相互导入。但如果从 level_one 模块内的测试程序调用,则不会,即使测试程序是由“发现”机制调用的。您对顶级包装器包的想法最终解决了我的各种导入问题(我需要 main/tests/lint 来完成所有工作)。
      【解决方案5】:

      这在库模块中起作用:

      import __main__ as main_package
      TOP_PACKAGE = main_package.__package__.split('.')[0]
      

      【讨论】:

        猜你喜欢
        • 2021-02-05
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 1970-01-01
        • 1970-01-01
        • 2013-01-11
        • 2019-08-11
        • 2021-12-04
        相关资源
        最近更新 更多