如何在包内引用Python的顶级模块?
在下面的层级结构中,有没有一种方便且通用的方法,可以在所有下面的.py文件中使用一个通用的术语来引用“top_package”?我想要一种一致的方式来导入其他模块,这样即使“top_package”的名字改变了,也不会出问题。
我不太喜欢使用相对导入,比如“..level_one_a”,因为相对路径在每个下面的Python文件中都会不同。我在寻找一种方法:
- 每个Python文件都可以对同一个模块使用相同的导入语句。
在包内的任何.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 个回答
你可以结合使用 __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
这段代码不依赖于顶层包的名字,可以在包的任何地方使用。不过,它看起来有点复杂。
把你的包和 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 = __import__(__name__.split('.')[0])
这里的关键是,对于每一个模块,__name__
这个变量会包含模块的完整路径,路径中的各个部分用点号分隔,比如说 top_package.level_one_a.my_lib
。所以,如果你想获取顶层包的名字,只需要取路径的第一个部分,然后用 __import__
来导入它。
尽管用来访问这个包的变量名仍然叫 top_package
,你可以给这个包换个名字,它依然能正常工作。