Python中__import__和import as的区别

5

我希望能够在Python中动态导入一个配置文件。

当使用以下代码时,我的程序可以正常工作:

import conf.config as config

但是当我使用以下代码时,它不能正常工作:
config = __import__("conf.config")

以下是两个示例程序及其运行结果:

示例程序1:

print("Hello, World!")

运行结果:

Hello, World!

示例程序2:

x = 5
y = 7
print(x + y)

运行结果:

12

#regularimport.py
import conf.config as config

def read_values(cfg):
    for varname in cfg.__dict__.keys():
        if varname.startswith('__'):
            continue
        value = getattr(cfg, varname)
        yield (varname, value)

for name,value in read_values(config):
    print "Current config: %s = %s" % (name, value)

结果:

$python regularimport.py

Current config: SETTING_TWO = another setting
Current config: SETTING_ONE = some setting

动态导入:

#dynamicimport.py
conf_str = "conf.config"
config = __import__(conf_str)

def read_values(cfg):
    for varname in cfg.__dict__.keys():
        if varname.startswith('__'):
            continue
        value = getattr(cfg, varname)
        yield (varname, value)

for name,value in read_values(config):
    print "Current config: %s = %s" % (name, value)

结果:

$ python dynamicimport.py
Current config: config = <module 'conf.config' from '/home/ubuntu/importex/conf/config.pyc'>

我的问题是为什么有这种差别?更重要的是,我如何使动态导入示例的工作方式与常规导入相同?


第一个问题是,为什么你要尝试使用__import__。你有看过文档吗?它们不仅解释了它,而且在顶部告诉你,如果你不想学习所有可怕的细节,你应该使用importlib.import_module - abarnert
2个回答

6

正如文档所解释的:

名称变量为package.module形式时,通常返回顶层包(第一个点之前的名称),而不是名称指定的模块。

因此,当你这样做:

config = __import__("conf.config")

这并不是同样的意思:

import conf.config as config

但实际上更像是这样的:
import conf.config
config = conf

为什么?
因为使用import语句绑定的是conf而不是conf.config。 (当然,在import foo as bar中,显然会绑定bar...但是__import__不应该被视为import foo as bar,而应该视为import foo的等价物)。文档有进一步解释。但是要点是你可能根本不应该使用__import__
在函数文档的最顶部,它说:
注意:这是一种高级函数,与importlib.import_module()不同,它在日常Python编程中不需要使用。
在底部,解释了__import__如何与包一起工作以及为什么会这样工作后,它说:
如果您只想按名称导入模块(可能在包内),请使用importlib.import_module()
所以,你可以猜到,简单的解决方案是使用importlib.import_module
如果你必须使用Python 2.6,在那里importlib不存在......嗯,就没有简单的解决方案。你可以根据imp自己构建类似import_module的函数。或者使用__import__,然后浏览sys.modules。或者__import__每个部分,然后通过结果getattr。还有其他各种hacky的方法。是的,这很糟糕——这就是3.0和2.7解决它的原因。
Python 2.6文档提供了第二个hack的示例。将其适应于您的情况:
__import__("conf.config")
config = sys.modules["conf.config"]

0

config = __import__("conf.config") 不等同于 import conf.config as config

例如:

>>> import os.path as path
>>> path
<module 'posixpath' from '/usr/lib/python2.7/posixpath.pyc'>

>>> __import__('os.path')
<module 'os' from '/usr/lib/python2.7/os.pyc'>

不要使用 __import__,而要使用importlib.import_module来获取子包/子模块。

>>> import importlib
>>> importlib.import_module('os.path')
<module 'posixpath' from '/usr/lib/python2.7/posixpath.pyc'>

1
关于你的第一个例子:你如何在*nix和Windows上同时运行一个控制台?(/usr vs C:) - Hyperboreus
@Hyperboreus,我不小心混淆了虚拟机和真实机器的输出。感谢您指出。 - falsetru

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