python之from 和import執行過程分析


原文鏈接: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’].

  1. 存在模塊B

    如果sys.modules有B這個鍵,就會獲取相對應的值,也就是modules對象,然后從模塊B的__dict__列表中查找並獲取名稱為D的對象,如果不存在,拋出異常.

    模塊B的__dict__列表可利用dir(B)來查看.

  2. 不存在模塊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答案

  1. 運行A.py,當執行到from B import D語句時,因為還沒有運行過B.py,所以sys.modules中沒有B這個鍵.會創建一個鍵B並賦值為模塊B對象,只不過此時模塊B對象是空的,里面什么都沒有;
  2. 然后,暫停執行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.
  3. 這時,會再次執行到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答案

  1. 運行A1.py,當執行到import B1語句時,因為還沒有運行過B1.py,所以sys.modules中沒有B1這個鍵.會創建一個鍵B1並賦值為模塊B1對象,只不過此時這個模塊對象是空的,里面什么都沒有;

  2. 然后,暫停執行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.

  3. 這時,會再次執行到import B1語句,由於在第一步時已經在sys.modules創建了模塊B1對象,模塊B1對象已經存在了,不需要執行B1.py,所以繼續執行A1.py中的其他內容.

    注意:此時模塊B1仍為空的,里面什么都沒有.

  4. 當執行到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()函數

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM