簡體   English   中英

為什么 Python 的 __import__ 需要 fromlist?

[英]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是一個包並且modmod2是該包中尚未導入的模塊,則這將不起作用,就像在第三種和第五種情況下一樣。 __import__()函數需要知道modmod2import語句想要訪問的名稱,以便它可以查看它們是否是模塊並嘗試導入它們。 所以調用更接近於:

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

這導致__import__()嘗試加載pkg.modpkg.mod2以及pkg (但如果modmod2不存在,則在__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 ,而BC是一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM