如何在包内引用 Python 中的***模块?
Posted
技术标签:
【中文标题】如何在包内引用 Python 中的***模块?【英文标题】:How to reference to the top-level module in Python inside a package? 【发布时间】:2012-02-01 03:23:04 【问题描述】:在下面的层次结构中,是否有一种方便且通用的方式来使用下面所有 .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
【问题讨论】:
为什么要引用顶层模块? (有时这个答案揭示了真正的问题/解决方案。) 我想让包尽可能地复用,这样当我重命名顶层模块时,一切仍然可以正常工作,而不必更改里面的每个导入名称。 【参考方案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_leve
l,那么您可以从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]
【讨论】:
以上是关于如何在包内引用 Python 中的***模块?的主要内容,如果未能解决你的问题,请参考以下文章