掩盖其包中的python子模块__init __。py

时间:2015-12-14 12:17:39

标签: python python-2.7 import module

短版

特定设置要求我在__init__.py中创建局部变量,以掩盖同一包中的模块。

E.g。变量y(在__init__.py的本地上下文中)将隐藏模块y.py。语句import x.y将产生局部变量,而不是加载模块。

如果您不想阅读具体设置,请向下滚动到问题;没有细节,这是可以理解的。

详细说明

我已经实现了一组Python 2.7包,每个包都需要单独的配置设置。为方便起见,我计划提供配置默认值每个包,任何使用其中一个包的人都可以在本地覆盖。

(其基本原理是在将应用程序部署到运行特定环境(服务器,工作站,笔记本电脑等)的计算机时分发默认设置,但同时允许覆盖配置而不会出现混乱启动本地存储库或重置代码更新的本地适应。)

目录结构示例是:

~/pkg/
  |
  +- package_a/
  |    |
  |    +- __init__.py
  |    +- mod_x.py
  |    +- mod_y.py
  |
  +- package_b/
  |    |
  |    +- __init__.py
  |    +- mod_z.py
  |
  +- config/
  |    |
  |    +- __init__.py
  |    +- package_a.py # Should locally override <pkg>_sample.py
  |    +- package_a_sample.py
  |    +- package_b_sample.py
  |
  +- test_this.py

我希望像常规模块导入那样访问config/下存储的设置,例如:

# ~/pkg/test_this.py
import config.package_a as cfg_a

...但让它隐式切换到覆盖文件(如果存在)。

我的解决方案

为了以某种方式自动化该过程,我动态创建指向正确配置文件导入的局部变量。使用imp包,我可以导入一个模块,并特别指定它。 (即,在运行时,您无法区分是否已加载<pkg>_sample.py<pkg>.py来提供配置。)

我最终得到了这个:

# ~/pkg/config/__init__.py
import os
import imp

__all__ = ['datastore']
_cfgbase = os.path.dirname(os.path.realpath(__file__))

for cfgmodule in __all__:
    if os.path.isfile(os.path.join(_cfgbase, cfgmodule + '.py')):
        locals()[cfgmodule] = imp.load_source(
            cfgmodule, os.path.join(_cfgbase, cfgmodule + '.py'))
    else:
        locals()[cfgmodule] = imp.load_source(
            cfgmodule, os.path.join(_cfgbase, cfgmodule + '_sample.py'))

这实际上会创建对所需源文件的本地引用(在<pkg>_sample.py中存在<pkg>.py时省略config/

如果使用from config import package_a as cfg_a,我可以在其他模块/脚本中使用它。

问题

基本上,这个问题可能会回到众所周知的import x.y vs from x import y - 这个问题上。

这里有区别。

我知道import x.y需要y作为模块。是否有任何可能性在其包__init__.py中隐藏模块并在导入时提供本地变量instad

  • from x import yy&#39; s x
  • 中产生局部变量__init__.py
  • import x.y 始终导入模块,即使y中存在本地变量__init__.py

我不能强迫每个人都使用以前的import语句,人们喜欢在他们的代码中使用后者。

这里有任何建议吗?

编辑:修正了标题。遗憾。

解决方案

感谢@ martijn-pieters指出sys.modules

实际上,我的方法在没有明确地将新导入添加到sys.modules的情况下可以完美地工作,因为我在正确命名新导入时失败了:

locals()[cfgmodule] j= imp.load_source(
    'config.' + cfgmodule, os.path.join(_cfgbase, cfgmodule + '.py'))

这解决了这个问题,因为它没有使用规范名称注册新的子模块(此处为:package_a),而是将其注册为我的config包的子模块

非常感谢!

1 个答案:

答案 0 :(得分:5)

import x.y并不需要y成为模块。 import x.y会在'x'结构中查找'x.y'sys.modules个键。如果两者都找到,则x绑定到sys.modules['x']。只有当'x.y'不存在时,Python才会寻找要加载的模块。

然后,诀窍是将y填入sys.modules

sys.modules['x.y'] = y