如何在包内引用Python的顶级模块?

31 投票
6 回答
40326 浏览
提问于 2025-04-17 09:25

在下面的层级结构中,有没有一种方便且通用的方法,可以在所有下面的.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
    

6 个回答

1

你可以结合使用 __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

这段代码不依赖于顶层包的名字,可以在包的任何地方使用。不过,它看起来有点复杂。

11

把你的包和 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 了。

22

这段代码应该能解决你的问题:

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

这里的关键是,对于每一个模块,__name__ 这个变量会包含模块的完整路径,路径中的各个部分用点号分隔,比如说 top_package.level_one_a.my_lib。所以,如果你想获取顶层包的名字,只需要取路径的第一个部分,然后用 __import__ 来导入它。

尽管用来访问这个包的变量名仍然叫 top_package,你可以给这个包换个名字,它依然能正常工作。

撰写回答