从包的__init__.py中屏蔽python子模块

jbn*_*dlr 5 python import module python-2.7

精简版

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

例如,变量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
Run Code Online (Sandbox Code Playgroud)

我想访问存储在config/常规模块导入下的设置,例如:

# ~/pkg/test_this.py
import config.package_a as cfg_a
Run Code Online (Sandbox Code Playgroud)

...但是如果它存在则隐式切换到覆盖文件.

我的解决方法

为了以某种方式自动化该过程,我动态创建指向正确配置文件导入的局部变量.使用该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'))
Run Code Online (Sandbox Code Playgroud)

这实际上创建了对所需源文件的本地引用(省略了<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 yyx's 得到局部变量__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'))
Run Code Online (Sandbox Code Playgroud)

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

非常感谢!

Mar*_*ers 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
Run Code Online (Sandbox Code Playgroud)