原文鏈接:http://blog.csdn.net/lis_12/article/details/52883729
問題1
同一個目錄下,有兩個Python文件,A.py,B.py
#A.py from B import D class C(object): pass #B.py from A import C class D(object): pass ''' 執行A.py 結果: Traceback (most recent call last): File "A.py", line 4, in <module> from B import D File "B.py", line 4, in <module> from A import C File "A.py", line 4, in <module> from B import D ImportError: cannot import name D '''
why?
from B import D的執行機制
可以利用sys.modules查看是否包含B模塊,如查看os模塊,輸入
>>> sys.modules[‘os’].
-
存在模塊B
如果sys.modules有B這個鍵,就會獲取相對應的值,也就是modules對象,然后從模塊B的__dict__列表中查找並獲取名稱為D的對象,如果不存在,拋出異常.
模塊B的__dict__列表可利用dir(B)來查看.
-
不存在模塊B
如果不存在B這個鍵的話,則會為B創建一個模塊對象,此時模塊對象的__dict__列表為空,然后在搜索路徑下查找並執行B.py,以填充模塊B對象的__dict__列表,然后從__dict__列表中查找名稱為D的對象,如果找不到,則拋出異常.
注:module ,模塊的意思
根據上面修改代碼,查看sys.modules變化情況
#A.py #!/usr/bin/python # -*- coding: utf-8 -*- import sys try: print 'module B: %s'%sys.modules['B'] except Exception,e: print u'沒有module B %s'%e try: print 'module A: %s'%sys.modules['A'] except Exception,e: print u'沒有module A %s'%e from B import D class C(object): pass #B.py from A import C class D(object): pass ''' 執行A.py 結果: 沒有module B 'B' 沒有module A 'A' module B: <module 'B' from 'B.pyc'> module A: <module 'A' from 'A.py'> Traceback (most recent call last): File "A.py", line 4, in <module> from B import D File "B.py", line 4, in <module> from A import C File "A.py", line 4, in <module> from B import D ImportError: cannot import name D
問題1答案
- 運行A.py,當執行到from B import D語句時,因為還沒有運行過B.py,所以sys.modules中沒有B這個鍵.會創建一個鍵B並賦值為模塊B對象,只不過此時模塊B對象是空的,里面什么都沒有;
- 然后,暫停執行A.py的其他語句,Python 在搜索路徑下查找B.py,找到同目錄下的B.py並運行,為了填充模塊B對象中的__dict__列表.當執行到from A import C時,也會檢查sys.modules中是否有名為A的模塊,但因為A.py還沒有讀取完,所以並沒有在sys.modules中緩存對應的信息.同樣,Python 會創建一個鍵A並賦值為空的模塊A對象.暫停執行B.py並尋找、從頭執行A.py.
- 這時,會再次執行到from B import D語句,由於在第一步時已經在sys.modules創建了鍵B的模塊B對象,所以直接獲取到,但此時模塊B對象中的__dict__列表還是空的,里面什么都沒有啊,所以找不到名為D的對象,拋出異常.
對照運行結果,是不是很符合- -!
(表述能力有限,多看代碼吧- -)
問題2 將上述from import 換成 import
#A1.py #!/usr/bin/python # -*- coding: utf-8 -*- import sys try: print 'module B1: %s'%sys.modules['B1'] except Exception,e: print u'沒有module B1 %s'%e try: print 'module A1: %s'%sys.modules['A1'] except Exception,e: print u'沒有module A1 %s'%e import B1 class C(object): pass try: B1.D() except Exception,e: print u'B中沒有屬性D',e #B1.py import A1 class D(object): pass ''' 執行A.py 結果: 沒有module B1 'B1' 沒有module A1 'A1' module B1: <module 'B1' from 'B1.pyc'> module A1: <module 'A1' from 'A1.py'> B中沒有屬性D 'module' object has no attribute 'D' '''
由程序結果可知,將from…import…改成import后,程序不會出錯了,但是調用B1.D()時為什么會拋出異常呢?
問題2答案
-
運行A1.py,當執行到import B1語句時,因為還沒有運行過B1.py,所以sys.modules中沒有B1這個鍵.會創建一個鍵B1並賦值為模塊B1對象,只不過此時這個模塊對象是空的,里面什么都沒有;
-
然后,暫停執行A1.py的其他語句,Python 在搜索路徑下查找B1.py,找到同目錄下的B1.py並運行,為了填充模塊B1對象中的__dict__列表.當執行到import A1時,也會檢查sys.modules中是否有名為A1的模塊,但因為A1.py還沒有讀取完,所以並沒有在sys.modules中緩存對應的信息.然后,同樣的,Python 會創建一個鍵A1並賦值為空的模塊A1對象.暫停執行B1.py並尋找、從頭執行A1.py.
-
這時,會再次執行到import B1語句,由於在第一步時已經在sys.modules創建了模塊B1對象,模塊B1對象已經存在了,不需要執行B1.py,所以繼續執行A1.py中的其他內容.
注意:此時模塊B1仍為空的,里面什么都沒有.
-
當執行到B1.D()時,由於在模塊B1對象的__dict__列表找不到名為D的對象,拋出異常.
問題3 修改問題1中的程序使的能正確運行
#A2.py from B2 import D class C(object): pass #B2.py class D(object): pass from A2 import C
簡單說明:
A2.py->B2.py->A2.py時,D屬性已經在B2的__dict__列表中了,所以不會報錯…雖然這樣寫不會報錯,但是強烈不推薦這么寫….
總結
當Python程序導入其他模塊時,要避免循環導入,不然總會出意向不到的問題….
注:循環導入,即A文件導入了B,B文件又導入了A.
重新導入模塊
如果更新了一個已經用import語句導入的模塊,內建函數reload()可以重新導入並運行更新后的模塊代碼.它需要一個模塊對象做為參數.例如:
import foo
... some code ...
reload(foo) # 重新導入 foo
在reload()運行之后的針對模塊的操作都會使用新導入代碼,不過reload()並不會更新使用舊模塊創建的對象,因此有可能出現新舊版本對象共存的情況。*注意* 使用C或C++編譯的模塊不能通過 reload() 函數來重新導入。記住一個原則,除非是在調試和開發過程中,否則不要使用reload()函數
