使用自定义加载函数时,模块中未定义Python模块

时间:2014-08-22 07:39:33

标签: python python-import

考虑以下程序来加载目录中的所有模块:

import pkgutil
import os.path
import sys

def load_all(directory):
    for loader, name, ispkg in pkgutil.walk_packages([directory]):
        loader.find_module(name).load_module(name)

if __name__ == '__main__':
    here = os.path.dirname(os.path.realpath(__file__))
    path = os.path.join(here, 'lib')
    sys.path.append(path)
    load_all(path)

现在考虑我们有以下文件。

LIB /魔/ imho.py:

"""This is an empty module.
"""

LIB /魔/ wtf.py:

import magic.imho
print "magic contents:", dir(magic)

LIB /魔/ __ INIT __ PY:

"Empty package init file"

执行上述程序时,输出:

magic contents: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__']

换句话说,尽管import magic.imho imho,但magic包上没有属性magic.imho,导致后来对$ PYTHONPATH=lib ipython Python 2.7.6 (default, Mar 22 2014, 22:59:38) Type "copyright", "credits" or "license" for more information. IPython 1.2.1 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: import magic.wtf magic contents: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'imho'] 的引用失败。

直接在python中运行相同的代码(或多或少)会显示不同的输出,这是我在运行加载程序时所期望的输出。

magic.imho

为什么会有区别?

更新:此处的重点是,magic.wft内的符号magic.wtf不存在,尽管它是明确导入的。因此,需要对自定义加载过程执行哪些操作才能使其正常运行并确保符号setattr中导入后

使用exec的解决方案: BartoszKP使用exec为解决方案提供了以下解决方案。由于我通常不喜欢使用setattr(由于很多原因,注入是第一个)我使用了rpartitiondef load_all(directory): for loader, name, ispkg in pkgutil.walk_packages([directory]): module = loader.find_module(name).load_module(name) pkg_name, _, mod_name = name.rpartition('.') if pkg_name: setattr(sys.modules[pkg], mod_name, module) ,如下所示:

{{1}}

1 个答案:

答案 0 :(得分:0)

正如this answer中建议更准确地模仿import正在做的事情,您应修改load_all方法,如下所示:

def load_all(directory):
    for loader, name, ispkg in pkgutil.walk_packages([directory]):
        module = loader.find_module(name).load_module(name)
        exec('%s = module' % name)

这种方式有效, wtf.py 将在magic内正确绑定名称。因此,下次导入magic时,将不会重新加载(因为此函数已将其放入sys.modules),但它将正确分配所有子模块。

这似乎缓解了import机制中的一些怪癖,我试图在下面进行诊断。


有效的区别在于,在第一种情况下,模块的加载顺序与第二种情况不同。当您向每个模块添加print语句时,您将在执行主脚本时看到以下内容:

  

加载魔法

     

加载imho

     

加载wtf

     

魔术内容:['__ builtin__','__ doc__','__ file __','_ _ 1 ___','__ package__','__ path __']

对于第二种情况:

  

加载魔法

     

加载wtf

     

加载imho

     

魔法内容:['__ builtin__','__ doc__','__ file __','_ _ 1 _','_ _ package__','__ path__','imho']

因此,在第一种情况下,{em> wtf.py 执行时已加载magicimho,并且import语句未重新加载已加载模块:

  

首先,如果模块已经存在于sys.modules中(如果在导入机制之外调用加载器的可能性)那么它将使用该模块进行初始化而不是新模块

如果您更改主脚本,例如:

if __name__ == '__main__':
    here = os.path.dirname(os.path.realpath(__file__))
    path = os.path.join(here, 'lib')
    sys.path.append(path)
    import magic.wtf

它与解释器完全相同。

如果您按照以下方式修改 wtf.py (为了演示目的),原始脚本也将按预期工作:

import sys

try:
    del sys.modules['magic.imho']
except:
    pass

import magic.imho

print "magic contents:", dir(magic)

或者像这样:

import sys
import magic.imho
magic.imho = sys.modules['magic.imho']

print "magic contents:", dir(magic)

输出:

  

加载魔法

     

加载imho

     

加载wtf

     

加载imho

     

魔法内容:['__ builtin__','__ doc__','__ file __','_ _ 1 _','_ _ package__','__ path__','imho']

注意:如果某个模块已加载,则sys.modules中存在一个模块,该模块与导入不同。在第一种情况下,它是已加载,但未导入。因此它存在于sys.modules中,但不存在于当前命名空间中。所以我猜它是由你的加载器加载而没有imho绑定它(所以它只是裸magic模块)。之后,当Python看到import magic.imho时,它会检查magicmagic.imho是否已加载(可由print sys.modules验证)并使用此版本的magic并对其进行绑定到局部变量magic


相关: