![](/img/trans.png)
[英]TypeError using string index for “fromlist” in Python __import__
[英]Why does Python's __import__ require fromlist?
在 Python 中,如果要以編程方式導入模塊,可以執行以下操作:
module = __import__('module_name')
如果你想導入一個子模塊,你會認為這很簡單:
module = __import__('module_name.submodule')
當然,這行不通; 您只需再次獲得module_name
。 你必須要做:
module = __import__('module_name.submodule', fromlist=['blah'])
為什么? 只要不為空, fromlist
的實際值似乎根本不重要。 要求一個參數,然后忽略它的值有什么意義?
Python 中的大多數事情似乎都有充分的理由,但對於我的生活,我無法為這種行為的存在提出任何合理的解釋。
事實上, __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__()
函數已經完成並遍歷名稱一樣,但這意味着要重復一些工作。 因此,當且僅當fromlist 被傳遞且不為空時,實現使__import__()
返回最右側的模塊而不是最左側的模塊。
(該import pkg as p
,並from pkg import mod as m
的語法並不了解這個故事,除了其本地的名字會被分配給任何改變-在__import__()
函數不會看到任何不同,當as
使用,這一切都保留在import
語句實現。)
看了答案還是覺得怪怪的,於是嘗試了下面的代碼示例。
首先,嘗試構建以下文件結構:
tmpdir
|A
|__init__.py
| B.py
| C.py
現在 A 是一個package
,而B
或C
是一個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']
結果顯示,當我們嘗試在module
而不是package
上使用 fromlist 時, fromlist 參數根本沒有幫助,因為module
已經編譯。 一旦導入,就無法忽略其他的。
答案可以在__import__
的文檔中__import__
:
fromlist 應該是
from name import ...
模擬from name import ...
列表,或者是要模擬import name
的空列表。從包中導入模塊時,注意
__import__('A.B', ...)
在 fromlist 為空時返回包 A,但在 fromlist 不為空時返回其子模塊 B。
所以基本上,這就是__import__
的實現方式:如果你想要子模塊,你傳遞一個fromlist
包含你想要從子模塊導入的東西,並且實現 if __import__
是這樣的,子模塊被返回。
進一步說明
我認為存在語義以便返回最相關的模塊。 換句話說,假設我有一個包foo
其中包含帶有功能baz
模塊bar
。 如果我:
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
模塊是foo.bar
。
第二種情況有點奇怪,但我猜它是用這種方式編寫的,以支持使用from <package> import <module>
語法from <package> import <module>
,在這種情況下, bar
仍然是要返回的最具體的模塊。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.