首页 > 解决方案 > 在 Python 包的 __init__.py 文件中,有没有办法检测包是否直接执行?

问题描述

我想要一种方法来检测我的模块是否是直接执行的,例如 inimport modulefrom module import *而不是 by import module.submodule(它也执行module),并且可以在module's中访问这些信息__init__.py

这是一个用例:

在 Python 中,一个常见的习惯用法是在模块__init__.py文件中添加 import 语句,例如“扁平化”模块的命名空间并使其子模块可以直接访问。不幸的是,这样做会使加载特定子模块非常慢,因为导入的所有其他同级__init__.py也会执行。

例如:

module/
   __init__.py
   submodule/
      __init__.py
      ...
   sibling/
      __init__.py
      ...

通过添加到module/__init__.py

from .submodule import *
from .sibling import *

现在,模块的用户可以在不知道包结构细节的情况下访问子模块中的定义(即from module import SomeClassSomeClass 在某处定义submodule并在其自己的__init__.py文件中公开)。

但是,如果我现在submodule直接运行(如import module.submodule通过调用python3 -m module.submodule,甚至通过 pytest 间接运行),我也将不可避免地执行sibling!如果兄弟姐妹很大,这可能会无缘无故减慢速度。

相反,我想写module/__init__.py一些类似的东西:

if __???__ == 'module':
   from .submodule import *
   from .sibling import *

Where__???__给了我导入的完全限定名称。任何类似的机制也可以工作,尽管我最感兴趣的是一般情况(检测直接执行)而不是这个具体的例子。

标签: pythonpython-3.xmoduleinit

解决方案


module当我们考虑导入系统实际上是如何工作的(如果它实际上是可能的)时,所期望的是会导致未定义的行为(从某种意义上说,扁平化的名称是否可以从 导入)。

假设地,如果您想要实现的目标是可能的,那么其中一些__dunder__会消除用于导入的 import 语句的歧义module/__init__.py(例如import moduleand from module import *、 vs import module.submodule。对于第一种情况,module可能会触发后续(慢)导入以产生“扁平化”版本所需的导入,而后一种情况 ( import module.submodule) 将避免这种情况,因此module不会包含任何“扁平化”导入的分配。

为了进一步说明该示例,假设可以通过简单地执行文件执行语句来创建该绑定来导入SiblingClassfrom 。但是,如果执行导致避免了 flatten 导入,我们会得到以下场景:module.sibling.SiblingClassfrom module import SiblingClassmodule/__init__.pyfrom .sibling import *import module.submodule

import module.submodule
# module.submodule gets imported
from module import SiblingClass
# ImportError will occur

这是为什么?这仅仅是由于 Python 导入文件的方式——源文件被完整执行一次,以将导入、函数和类声明分配给指定的名称,并sys.modules在其导入名称下注册。再次导入模块将不会再次执行该文件,因此如果该from .sibling import *语句在其初始导入期间未执行(即import module.submodule),则在后续导入同一模块期间将永远不会再次执行,因为初始导入生成的副本分配给返回其模块条目sys.module(除非手动重新加载模块,否则将再次执行该模块的代码)。

您可以通过将语句放入文件中来验证这一事实print,导入相应的模块以查看产生的输出,并看到该模块的后续导入不会产生进一步的输出(相关:当一个模块被导入两次时会发生什么? )。

实际上,问题中描述的所需功能无法在 Python 中实现。

关于这个主题的一个相关线程:How to import sub module without exec __init__.py in package


推荐阅读