2011-06-15 105 views
31
相對進口

假設我們有兩個模塊循環依賴:循環模塊依賴,並在Python

# a.py 
import b 
def f(): return b.y 
x = 42 

# b.py 
import a 
def g(): return a.x 
y = 43 

這兩個模塊在目錄pkg用空__init__.py。導入pkg.apkg.b正常工作,如this answer中所述。如果我改變了進口相對進口

from . import b 

我得到一個ImportError試圖導入一個模塊時:

>>> import pkg.a 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "pkg/a.py", line 1, in <module> 
    from . import b 
    File "pkg/b.py", line 1, in <module> 
    from . import a 
ImportError: cannot import name a 

爲什麼我得到這個錯誤?情況與上述情況不一樣嗎? (這是否與​​有關?)

編輯:此問題與軟件設計無關。我意識到避免循環依賴的方法,但我仍然對錯誤的原因感興趣。

+0

您可以指定您在更改爲from時收到的'ImportError'的詳細信息。進口b'? – 2011-06-15 01:03:44

+0

你如何練習這段代碼?你有'pkg'中的另外一個模塊,它的功能如下:'if __name__ =='main':from。導入一個'? 如果是這樣,您可能需要閱讀[PEP 366](http://python.org/dev/peps/pep-0366/),這個PEP可以更容易地使用包中的可執行模塊的相對導入。 – 2011-06-15 01:09:03

+2

如果__a__依賴於__b__,而__b__依賴於__a__,爲什麼不加入同一個文件? – JBernardo 2011-06-15 01:14:21

回答

29

首先讓我們先從如何在python from import工作:

那麼首先讓我們來看看字節碼:

>>> def foo(): 
...  from foo import bar 

>>> dis.dis(foo) 
2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    2 (('bar',)) 
       6 IMPORT_NAME    0 (foo) 
       9 IMPORT_FROM    1 (bar) 
      12 STORE_FAST    0 (bar) 
      15 POP_TOP    
      16 LOAD_CONST    0 (None) 
      19 RETURN_VALUE   

嗯有趣:),所以from foo import bar被翻譯成第一個IMPORT_NAME foo,相當於import foo,然後IMPORT_FROM bar

現在什麼IMPORT_FROM做什麼?

讓我們看看蟒蛇做時,他發現IMPORT_FROM

TARGET(IMPORT_FROM) 
    w = GETITEM(names, oparg); 
    v = TOP(); 
    READ_TIMESTAMP(intr0); 
    x = import_from(v, w); 
    READ_TIMESTAMP(intr1); 
    PUSH(x); 
    if (x != NULL) DISPATCH(); 
    break; 

嗯,基本上他拿到的名字進口而來,這是我們foo()功能將bar,然後他從框架棧中彈出這是執行的最後一個操作碼這是IMPORT_NAME的返回值v,然後調用函數import_from()這個兩個參數:

static PyObject * 
import_from(PyObject *v, PyObject *name) 
{ 
    PyObject *x; 

    x = PyObject_GetAttr(v, name); 

    if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { 
     PyErr_Format(PyExc_ImportError, "cannot import name %S", name); 
    } 
    return x; 
} 

正如您所見,import_from()函數很安靜,它首先嚐試從模塊v中獲取屬性name,如果它不存在,則會引發ImportError,否則返回此屬性。

現在這與相對導入有什麼關係?

好像from . import b這樣的相對導入例如在OP問題到from pkg import b的情況下是等價的。

但這是怎麼發生的?爲了理解這一點,我們應該看看Python的import.c模塊,特別是函數get_parent()。正如你所看到的,這裏列出的函數很安靜,但總的來說,它看到一個相對導入時的作用是試圖用父包替換點.,這取決於__main__模塊,這又是來自OP問題的是包pkg

現在讓我們把所有這些放在一起,並試圖找出爲什麼我們最終在OP問題中的行爲。

爲此,它將幫助我們,如果我們可以看到什麼python做進口時,這是我們的幸運日python已與此功能,可以通過運行它在額外的詳細模式-vv啓用。

那麼使用命令行:python -vv -c 'import pkg.b'

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 

import pkg # directory pkg 
# trying pkg/__init__.so 
# trying pkg/__init__module.so 
# trying pkg/__init__.py 
# pkg/__init__.pyc matches pkg/__init__.py 
import pkg # precompiled from pkg/__init__.pyc 
# trying pkg/b.so 
# trying pkg/bmodule.so 
# trying pkg/b.py 
# pkg/b.pyc matches pkg/b.py 
import pkg.b # precompiled from pkg/b.pyc 
# trying pkg/a.so 
# trying pkg/amodule.so 
# trying pkg/a.py 
# pkg/a.pyc matches pkg/a.py 
import pkg.a # precompiled from pkg/a.pyc 
# clear[2] __name__ 
# clear[2] __file__ 
# clear[2] __package__ 
# clear[2] __name__ 
# clear[2] __file__ 
# clear[2] __package__ 
... 
Traceback (most recent call last): 
    File "<string>", line 1, in <module> 
    File "pkg/b.py", line 1, in <module> 
    from . import a 
    File "pkg/a.py", line 2, in <module> 
    from . import a 
ImportError: cannot import name a 
# clear __builtin__._ 

HMM的ImportError前什麼只是發生?

前)在pkg/b.pyfrom . import a被調用時,上面from pkg import a,這又是在字節碼解釋爲被翻譯相當於import pkg; getattr(pkg, 'a')。但是等一下a是個模塊嗎?! 好吧,這裏有趣的部分,如果我們有像from module|package import module在這種情況下會發生第二次導入,這是導入子句中模塊的導入。因此,在OP示例中,我們現在需要導入pkg/a.py,正如您首先知道的,我們在我們的sys.modules中設置了一個新模塊的密鑰,該模塊將爲pkg.a,然後我們繼續解釋模塊pkg/a.py,但在模塊pkg/a.py完成導入,請致電from . import b

現在來到了二)部分,pkg/b.py將被導入,並在其中打開它會首先嚐試import pkg這是因爲pkg已經導入所以我們sys.modules關鍵pkg它只會返回該值鍵。然後將import b設置pkg.bsys.modules並開始解釋。我們到達這條線from . import a

記得pkg/a.py已經導入,意爲('pkg.a' in sys.modules) == True,因此進口將被跳過,並且只有getattr(pkg, 'a')將被調用,但會發生什麼? python沒有完成導入pkg/a.py!?因此只會調用getattr(pkg, 'a'),這將在import_from()函數中產生AttributeError,該函數將被翻譯爲ImportError(cannot import name a)

免責聲明:這是我自己努力去理解口譯員內部發生的事情,我遠不是專家。

編輯:這個答案改寫,因爲當我試着看了一遍我說我的答案是如何制定不好,現在希望這將是更爲有用:)

+0

感謝您提供詳細的解答並真正瞭解來源。我仍然不是很滿意。有兩件事讓我困惑:1.在你的觀點(1)中,執行'from pkg import b'。爲什麼即使'b'顯然不在'pkg'的命名空間中,它也能工作?這與下一步的情況不一樣,'從pkg import a'? 2.模塊的循環依賴性通常工作,因爲在執行模塊主體之前,將空模塊對象插入到'sys.modules'中。爲什麼在執行模塊體之前,沒有在'pkg'中插入空模塊對象'a'? – 2011-06-16 21:31:39

+0

@seven:1.因爲'pkg.b'不是'sys.modules',所以我們需要先導入它,在'sys.modules'中設置條目'pkg.b',然後在導入成功後添加'b'到pkg的命名空間。 2.'a'模塊被插入到包名稱空間中的導入結尾處,因爲如果導入'a'失敗(您可以在模塊中引發異常並查看),我們不希望它被掛在'pkg'的命名空間。基本上,導入到包名稱空間的模塊添加是在導入成功後完成的。 – mouad 2011-06-16 21:51:53

+0

感謝您爲此答案付出的所有努力。現在它清楚瞭解發生了什麼。 – 2011-06-23 16:05:48

4

(Incedentally,相對進口無所謂。使用​​...揭示了同樣的異常。)

我想是怎麼回事是from foo import barimport foo.bar之間的區別是,在第一個,值bar可能是pkg foo中的模塊,或者它可能是模塊foo中的變量。在第二種情況下,bar不適用於模塊/包。

這很重要,因爲如果bar被認爲是一個模塊,那麼sys.modules的內容就足以填充它。如果它可能是foo模塊中的一個變量,那麼解釋器必須真正查看foo的內容,但是在導入foo時,這將是無效的;實際的模塊還沒有被填充。

在相對導入的情況下,我們理解from . import bar意味着從包含當前模塊的包導入bar模塊,但這實際上只是語法糖,.名稱被轉換爲完全限定的名稱並通過到__import__(),因此它看起來全球所有喜歡ambigious from foo import bar

+0

感謝您的回答。不幸的是,我認爲這不能解釋它。如果在導入時查看'foo'的內容是無效的,那麼非循環導入也會失敗:如果'foo.bar'確實從'foo import quux'中導入了'foo.bar',解釋器在導入時也必須查看'foo'的內容,這樣可以正常工作。我有道理嗎? – 2011-06-15 11:31:58

1

作爲額外注:

我有以下模塊結構:

base 
+guiStuff 
    -gui 
+databaseStuff 
    -db 
-basescript 

我希望能夠通過import base.basescript運行我的腳本,但是這個失敗的錯誤,因爲gui文件有一個導致導入baseimport base.databaseStuff.db。由於base僅被註冊爲__main__,因此導致整個導入和上述錯誤的第二次執行,除非我使用了高於base的外部腳本,因此僅導入base/basescript一次。爲了防止出現這種情況,我把以下內容放在我的腳本中:

if __name__ == '__main__' or \ 
    not '__main__' in sys.modules or \ 
    sys.modules['__main__'].__file__ != __file__: 
    #imports here