为什么Python的__import__需要fromlist?
在Python中,如果你想通过代码来导入一个模块,可以这样做:
module = __import__('module_name')
如果你想导入一个子模块,你可能会认为只需要简单地这样做:
module = __import__('module_name.submodule')
当然,这样做是行不通的;你只会得到 module_name
。你必须这样做:
module = __import__('module_name.submodule', fromlist=['blah'])
为什么呢? 其实,fromlist
的实际值似乎根本不重要,只要它不是空的。那么,要求提供一个参数的意义是什么呢?
在Python中,大多数事情都有其合理的原因,但我实在想不出为什么会有这种行为存在。
3 个回答
答案可以在 __import__
的文档中找到:
fromlist 应该是一个名字的列表,用来模拟
from name import ...
,或者是一个空列表,用来模拟import name
。当从一个包中导入模块时,注意
__import__('A.B', ...)
在 fromlist 为空时返回包 A,但当 fromlist 不为空时返回它的子模块 B。
简单来说,这就是 __import__
的实现方式:如果你想要子模块,就传一个 fromlist
,里面包含你想从子模块中导入的内容,这样 __import__
就会返回这个子模块。
进一步解释
我觉得这样设计是为了返回最相关的模块。换句话说,假设我有一个包 foo
,里面有一个模块 bar
,而这个模块里有一个函数 baz
。如果我:
import foo.bar
那么我可以这样引用 baz
:
foo.bar.baz()
这就像是 __import__("foo.bar", fromlist=[])
。
如果我用:
from foo import bar
那么我可以这样引用 baz
:
这就类似于 __import__("foo.bar", fromlist=["something"])
。
如果我这样做:
from foo.bar import baz
那么我可以这样引用 baz
:
baz()
这就像是 __import__("foo.bar", fromlist=["baz"])
。
所以在第一种情况下,我必须使用完整的名称,因此 __import__
返回的是你用来引用导入元素的第一个模块名,也就是 foo
。在最后一种情况下,bar
是包含导入元素的最具体的模块,所以 __import__
返回 foo.bar
模块是有道理的。
第二种情况有点奇怪,但我猜这样写是为了支持使用 from <package> import <module>
的语法,在这种情况下 bar
仍然是最具体的模块。
我读到这个答案时感觉有点奇怪,所以我尝试了下面的代码示例。
首先,试着建立下面的文件结构:
tmpdir
|A
|__init__.py
| B.py
| C.py
现在 A 是一个 包
,而 B
或 C
是一个 模块
。所以当我们在 ipython 中尝试一些像这样的代码时:
接下来,在 ipython 中运行示例代码:
In [2]: kk = __import__('A',fromlist=['B'])
In [3]: dir(kk)
Out[3]:
['B',
'__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'__path__']
看起来 fromlist 的功能和我们预期的一样。但当我们尝试在一个 模块
上做同样的事情时,情况就变得奇怪了。假设我们有一个叫 C.py 的模块,里面的代码是:
handlers = {}
def hello():
print "hello"
test_list = []
现在我们尝试对它做同样的事情。
In [1]: ls
C.py
In [2]: kk = __import__('C')
In [3]: dir(kk)
Out[3]:
['__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'handlers',
'hello',
'test_list']
那么当我们只想导入 test_list 时,这样做有效吗?
In [1]: kk = __import__('C',fromlist=['test_list'])
In [2]: dir(kk)
Out[2]:
['__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'handlers',
'hello',
'test_list']
结果显示,当我们在一个 模块
上使用 fromlist,而不是在一个 包
上时,fromlist 参数根本没有帮助,因为 模块
已经被编译过了。一旦它被导入,就没有办法忽略其他的内容。
其实,__import__()
的行为完全是因为import
语句的实现,它会调用__import__()
。基本上,__import__()
可以通过import
被调用五种略有不同的方式(主要分为两类):
import pkg
import pkg.mod
from pkg import mod, mod2
from pkg.mod import func, func2
from pkg.mod import submod
在第一种和第二种情况下,import
语句应该把“最左边”的模块对象赋值给“最左边”的名称:pkg
。在执行import pkg.mod
后,你可以使用pkg.mod.func()
,因为import
语句引入了本地名称pkg
,它是一个模块对象,并且有一个mod
属性。所以,__import__()
函数必须返回“最左边”的模块对象,以便可以赋值给pkg
。因此,这两个导入语句可以理解为:
pkg = __import__('pkg')
pkg = __import__('pkg.mod')
在第三、第四和第五种情况下,import
语句需要做更多的工作:它需要将多个名称(可能是多个)赋值,这些名称需要从模块对象中获取。__import__()
函数只能返回一个对象,而且没有必要让它从模块对象中获取每一个名称(这样会让实现变得复杂得多)。所以简单的做法可能是这样的(针对第三种情况):
tmp = __import__('pkg')
mod = tmp.mod
mod2 = tmp.mod2
不过,如果pkg
是一个包,而mod
或mod2
是这个包中的模块(而且这些模块还没有被导入),那么这个方法就不行了,就像在第三和第五种情况下那样。__import__()
函数需要知道mod
和mod2
是import
语句想要访问的名称,这样它才能检查这些名称是否是模块,并尝试导入它们。所以调用应该更接近于:
tmp = __import__('pkg', fromlist=['mod', 'mod2'])
mod = tmp.mod
mod2 = tmp.mod2
这会导致__import__()
尝试加载pkg.mod
和pkg.mod2
以及pkg
(但如果mod
或mod2
不存在,这在__import__()
调用中并不是错误;产生错误的责任留给import
语句)。但这仍然不适用于第四和第五个例子,因为如果调用是这样:
tmp = __import__('pkg.mod', fromlist=['submod'])
submod = tmp.submod
那么tmp
最终会变成pkg
,而不是你想要获取submod
属性的pkg.mod
模块。实现可以选择让import
语句做额外的工作,像__import__()
函数那样在.
上拆分包名并遍历名称,但这会导致一些工作重复。因此,最终的实现让__import__()
返回最右边的模块,而不是最左边的模块,前提是传入了非空的fromlist。
(import pkg as p
和from pkg import mod as m
的语法并没有改变这个故事,只是改变了赋值给哪些本地名称——当使用as
时,__import__()
函数并没有看到什么不同,所有内容仍然在import
语句的实现中。)