Python中的__import__函数无法按预期工作

44

在使用带有点号的名称(比如:somepackage.somemodule)进行__import__导入时,返回的模块不是somemodule,而似乎大部分都是空的!这是怎么回事?

6个回答

54

来自 Python 文档关于 __import__ 的说明:

__import__( name[, globals[, locals[, fromlist[, level]]]])

当名字变量的形式为 package.module 时,通常会返回顶层包(从开头到第一个点之前的名称),而不是名为 name 的模块。但是,如果给出了非空的 fromlist 参数,则会返回名为 name 的模块。这样做是为了与生成不同类型的导入语句的字节码兼容;当使用 "import spam.ham.eggs" 时,顶层包 spam 必须放在导入命名空间中,但是当使用 "from spam.ham import eggs" 时,必须使用 spam.ham 子包来查找 eggs 变量。作为对此行为的解决方法,可以使用 getattr() 提取所需组件。例如,您可以定义以下辅助程序:

def my_import(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod
当你请求somepackage.somemodule时,__import__会返回somepackage.__init__.py,它通常是空的。fromlist参数可以使其返回somemodule(在fromlist中列出你要的变量名列表,但实际上不会返回这些变量名)。
你也可以像我一样使用他们建议的函数。
注意:我问这个问题完全是为了自己回答。我的代码有一个严重的bug,因为我误诊了它,花费了很长时间才找到问题所在,所以我觉得我应该帮助Stack Overflow社区,并在这里发布我遇到的坑。

32

Python 2.7中有importlib,点分路径按预期解析

import importlib
foo = importlib.import_module('a.dotted.path')
instance = foo.SomeClass()

2
还有一个Python 2.6的包,可以将此功能移植回来。 https://pypi.python.org/pypi/importlib/ - melinath

16

有一个更简单的解决方案,如文档所述:

如果你只是想通过名称导入一个模块(可能在包内),你可以调用 __import__() 然后在 sys.modules 中查找它:

>>> import sys
>>> name = 'foo.bar.baz'
>>> __import__(name)
<module 'foo' from ...>
>>> baz = sys.modules[name]
>>> baz
<module 'foo.bar.baz' from ...>

7

有一样东西可以按照你的意愿工作:twisted.python.reflect.namedAny

>>> from twisted.python.reflect import namedAny
>>> namedAny("operator.eq")
<built-in function eq>
>>> namedAny("pysqlite2.dbapi2.connect")
<built-in function connect>
>>> namedAny("os")
<module 'os' from '/usr/lib/python2.5/os.pyc'>

这非常有用,但是我在我的程序中实际上没有其他需要使用Twisted。尽管作为创始人(!),您可能比我更了解其可能性(从未使用过)。 - dwestbrook

1

对于Python 2.6,我编写了以下代码片段:

def import_and_get_mod(str, parent_mod=None):
    """Attempts to import the supplied string as a module.
    Returns the module that was imported."""
    mods = str.split('.')
    child_mod_str = '.'.join(mods[1:])
    if parent_mod is None:
        if len(mods) > 1:
            #First time this function is called; import the module
            #__import__() will only return the top level module
            return import_and_get_mod(child_mod_str, __import__(str))
        else:
            return __import__(str)
    else:
        mod = getattr(parent_mod, mods[0])
        if len(mods) > 1:
            #We're not yet at the intended module; drill down
            return import_and_get_mod(child_mod_str, mod)
        else:
            return mod

0

我的做法是

foo = __import__('foo',  globals(), locals(), ["bar"], -1)
foobar = eval("foo.bar")

那么我就可以从我的任何内容中访问了

foobar.functionName()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接