为什么Python的__import__需要fromlist?


76

在Python中,如果要以编程方式导入模块,则可以执行以下操作:

module = __import__('module_name')

如果要导入子模块,您会认为这很简单:

module = __import__('module_name.submodule')

当然,这是行不通的。你module_name又得到了。你所要做的:

module = __import__('module_name.submodule', fromlist=['blah'])

为什么?fromlist只要它不是空的,它的实际值就似乎无关紧要。要求参数然后忽略其值的意义何在?

Python中的大多数内容似乎都是有充分的理由完成的,但是出于我的一生,对于这种行为的存在,我无法提出任何合理的解释。

Answers:


127

实际上,的行为__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__()功能需要知道modmod2是名字的import声明都希望能够获得访问,以便它可以看到,如果他们是模块和尝试导入他们。因此,电话更接近:

tmp = __import__('pkg', fromlist=['mod', 'mod2'])
mod = tmp.mod
mod2 = tmp.mod2

这会导致__import__()尝试和负载pkg.modpkg.mod2以及pkg(但如果mod还是mod2不存在,它不是在错误__import__()的呼叫;产生一个错误是留给import。声明),但还不是第四和正确的事第五个示例,因为如果调用是这样的:

tmp = __import__('pkg.mod', fromlist=['submod'])
submod = tmp.submod

那么tmp最终将pkg像以前一样是,而不是pkg.modsubmod要从中获取属性的模块。该实现可能已经决定要这样做,所以该import语句会做更多的工作,.__import__()已经在函数中那样拆分程序包名称并遍历名称,但这将意味着重复一些工作。所以,相反,执行方面取得__import__()返回最右边的模块,而不是最左边的一个当且仅当fromlist里传递,而不是空。

import pkg as pandfrom pkg import mod as m语法对这个故事没有任何改变,只是将本地名称分配给了它-使用该__import__()函数时as并没有什么不同,它们都保留在import语句实现中。)


5

当我阅读答案时,我仍然感到很奇怪,因此我尝试了以下代码示例。

首先,尝试构建以下文件结构:

tmpdir
  |A
     |__init__.py
     | B.py
     | C.py

现在A是一个package,而BorC是一个module。因此,当我们在ipython中尝试类似以下代码时:

其次,在ipython中运行示例代码:

  In [2]: kk = __import__('A',fromlist=['B'])

  In [3]: dir(kk)
  Out[3]: 
  ['B',
   '__builtins__',
   '__doc__',
   '__file__',
   '__name__',
   '__package__',
   '__path__']

好像fromlist像我们期望的那样工作。但是当我们尝试在桌面上做同样的事情时,事情就变得混乱了module。假设我们有一个名为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']

结果显示,当我们尝试在frommodule而不是a上使用fromlist时package,fromlist参数根本没有帮助,因为module已经编译过了。导入后,将无法忽略其他对象。


2

可以在文档中找到答案__import__

fromlist应该是要模拟的名称列表from name import ...,或者是要模拟的空列表import name

从包导入模块时,请注意,__import__('A.B', ...)如果fromlist为空,则返回包A,但如果fromlist不为空,则返回其子模块B。

因此,基本上,这就是工作的实现方式__import__:如果需要子模块,则传递一个fromlist包含要从子模块导入的内容,如果实现则__import__返回子模块。

进一步说明

我认为存在语义,以便返回最相关的模块。换句话说,说我有一个foo包含bar带有function模块的包baz。如果我:

import foo.bar

然后我指的baz

foo.bar.baz()

这就像__import__("foo.bar", fromlist=[])

如果相反,我导入:

from foo import bar

然后我baz称为bar.baz()

这将类似于__imoort__("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它仍然是返回的最特定的模块。


说“这就是实现的工作原理”并不能回答我的问题。为什么这样工作?说“模仿来自名称导入……”的形式比较接近,但是在什么情况下您需要它呢?fromlist与导入的实际工作方式没有任何区别,因此我看不到在什么情况下需要传递它来模拟任何东西,除了函数的明显行为应该是什么。
ieure

1
您是对的,这就是问题所在。我更新了答案以提供更相关的答复。
mipadi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.