从 __init__.py 中导入的模块导入

import from a module imported in __init__.py

我以前有以下结构(作为更大项目的一部分):

lib/
  __init__.py
  somemodule.py

其中 __init__.py 为空,somemodule.py 包含一些方法 def hello()。现在,在我的所有代码存储库中,都有大量该方法的导入,特别是 from lib.somemodule import hello.

由于各种原因,我现在想somemodule.py移动到一个分包中。意思是,我希望结构为:

lib/
  sublib/
    __init__.py
    somemodule.py
  __init__.py

并认为如果现在 lib/__init__.py 将包含: from sublib import somemodule、运行ning from lib.somemodule import hello 仍然有效(本质上,暂时改变结构 "transparent")。

但是,如果某些外部代码 运行s from lib.somemodule import hello,则会引发错误 ImportError: No module named somemodule,而 运行ning from lib import somemodule 有效。

如果您能解释我做错了什么,以及关于如何在文件夹结构发生变化的情况下 from lib.somemodule import hello 运行 的任何建议,我将不胜感激。

如果你想让 from lib.somemodule import hello 工作,你需要在你的包 lib 中有一个名为 somemodule 的模块。调用 from sublib import somemodule 只会将您的模块添加到包的范围内,而不会定义导入机制可以找到的新模块。这也是 from lib import somemodule 起作用的原因。它只从包 lib.

导入全局变量 somemodule

所以有一个包含内容

的模块lib/somemodule.py
from .sublib.somemodule import *

将是解决该问题的最简单、最干净的方法,可能包括某种 DeprecationWarning。然后在以后的某个时间删除它。

可以做到

以下想法基于 the import statement 文档中的信息。

The first place checked is sys.modules, the cache of all modules that have been imported previously.

所以一个简单的可以做到的解决方案是自己编写模块缓存:

import sys
from .sublib import somemodule
sys.modules[__package__+'.somemodule'] = somemodule

也许在 sys.meta_path 中添加一个查找器可能会更干净一些:

import sys
from .sublib import somemodule
class SomemoduleFinder(object):
    def find_module(self, fullname, path=None):
        if fullname == __package__+'.somemodule':
            return self
    def load_module(self, fullname):
        return somemodule
sys.meta_path.append(SomemoduleFinder())

如果您之前有一个设置,其中 lib/__init__.py 没有直接导入 somemodule,您也可以将导入放在 SomemoduleFinder.load_module 方法中以反映该行为。虽然这只适用于 Python 2 对我来说。

另外

查看 Python Module Imports - Explicit vs Implicit Relative Imports 以防止将来移植到 Python 时出现可移植性问题 3.