(轉)Python中的模組迴圈匯入問題

來源:互聯網
上載者:User

標籤:就會   href   編譯   none   python   google   glob   exist   bsp   

 本文轉自: https://wiki.woodpecker.org.cn/moin/MiscItems/2008-11-25問題

 cleven <[email protected]>

回覆至     [email protected]收件者     [email protected]日期      2008年11月25日 下午 12:01主旨      [CPyUG:72341] import嵌套的問題

看了《Python源碼剖析》,裡面提到的嵌套import的問題還是沒有弄明白,各位給看一下吧。

[A.py]from B import Dclass C:pass[B.py]from A import Cclass D:pass

為什麼執行A的時候不能載入D呢?

如果將A.py改為:import B就可以了。

這是怎麼回事呢?

 

Robert Chen:詳解

 

Robert Chen <[email protected]om>回覆至     [email protected]收件者     [email protected]日期      2008年11月25日 下午 1:41主旨      [CPyUG:72362] Re: import嵌套的問題

恩,這跟Python內部import的機制是有關的,具體到from B import D,Python內部會分成幾個步驟:

  1. 在sys.modules中尋找符號"B"
  2. 如果符號B存在,則獲得符號B對應的module對象<module B>

    • 從<module B>的__dict__中獲得符號"D"對應的對象,如果"D"不存在,則拋出異常

  3. 如果符號B不存在,則建立一個新的module對象<module B>,注意,這時,module對象的__dict__為空白

    • 執行B.py中的運算式,填充<module B>的__dict__

    • 從<module B>的__dict__中獲得"D"對應的對象,如果"D"不存在,則拋出異常

所以,這個例子的執行順序如下:

1、執行A.py中的from B import D    由於是執行的python A.py,所以在sys.modules中並沒有<module B>存在,    首先為B.py建立一個module對象(<module B>),      注意,這時建立的這個module對象是空的,裡邊啥也沒有,    在Python內部建立了這個module對象之後,就會解析執行B.py,其目的是填充<module B>這個dict。2、執行B.py中的from A import C    在執行B.py的過程中,會碰到這一句,    首先檢查sys.modules這個module緩衝中是否已經存在<module A>了,    由於這時緩衝還沒有緩衝<module A>,    所以類似的,Python內部會為A.py建立一個module對象(<module A>),    然後,同樣地,執行A.py中的語句3、再次執行A.py中的from B import D    這時,由於在第1步時,建立的<module B>對象已經緩衝在了sys.modules中,    所以直接就得到了<module B>,    但是,注意,從整個過程來看,我們知道,這時<module B>還是一個空的對象,裡面啥也沒有,    所以從這個module中獲得符號"D"的操作就會拋出異常。    如果這裡只是import B,由於"B"這個符號在sys.modules中已經存在,所以是不會拋出異常的。

 

ZQ:圖解

  • JUDE繪製:PyImportFlow.jude

 

編譯追蹤

hiter的日記:

  • python的import的研究

問題代碼如下:

A.pyfrom A import Bclass B(object):pass>>> import ATraceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/home/john/pythonstudy/mypython/bin/A.py", line 9, in <module>    from A import BImportError: cannot import name B>>>

閱讀代碼後發現: 位元組碼大概

9           0 LOAD_CONST               0 (-1)              3 LOAD_CONST               1 ((‘B‘,))              6 IMPORT_NAME              0 (A)                   9 IMPORT_FROM              1 (B)             12 STORE_NAME               1 (B)             15 POP_TOP 10          16 LOAD_CONST               2 (‘B‘)             19 LOAD_NAME                2 (object)             22 BUILD_TUPLE              1             25 LOAD_CONST               3 (<code object B at 0xb7a1fa88, file "A.py", line 10>)             28 MAKE_FUNCTION            0             31 CALL_FUNCTION            0             34 BUILD_CLASS             35 STORE_NAME               1 (B) 12          38 LOAD_CONST               4 (‘hi‘)             41 PRINT_ITEM             42 PRINT_NEWLINE             43 LOAD_CONST               5 (None)             46 RETURN_VALUE
  • 可以看出整個import的過程是:先import A,然後再import A然後報錯。
  • 經過分析發現原因是:在import A時,虛擬機器發現sys.modules(在import_submodule中會做檢查)中沒有載入過A,然後建立了一個A的module,建立的module是空的,需要向裡面加入__builtin__,__file__等屬性(在執行下一個import的時候,新module的dict將作為globals(locals)傳給執行(A)位元組碼時使用),然後虛擬機器會將這個新的module加入sys.modules中,至此虛擬機器的呼叫堆疊如下:(程式碼號可能不對,因為源碼中加入了很多調試輸出代碼)

 

#0  PyImport_AddModule (name=0xbfd91673 "A") at Python/import.c:617                                                            <-------PyImport_AddModule 在這裡#1  0x08106271 in PyImport_ExecCodeModuleEx (name=0xbfd91673 "A", co=0xb7da6748, pathname=0xbfd8f533 "A.pyc") at Python/import.c:653#2  0x08106c67 in load_source_module (name=0xbfd91673 "A", pathname=0xbfd8f533 "A.pyc", fp=0x821bd60) at Python/import.c:963#3  0x081085cf in load_module (name=0xbfd91673 "A", fp=0x821bd60, buf=0xbfd905d3 "A.py", type=1, loader=0x0) at Python/import.c:1753#4  0x0810a39b in import_submodule (mod=0x818c888, subname=0xbfd91673 "A", fullname=0xbfd91673 "A") at Python/import.c:2433    <--------import_submodule 在這裡#5  0x081098bb in load_next (mod=0x818c888, altmod=0x818c888, p_name=0xbfd91654, buf=0xbfd91673 "A", p_buflen=0xbfd9166c)    at Python/import.c:2234#6  0x08108e1c in import_module_level (name=0x0, globals=0xb7de82b4, locals=0xb7de82b4, fromlist=0x818c888, level=-1) at Python/import.c:2005#7  0x081093a1 in PyImport_ImportModuleLevel (name=0xb7de115c "A", globals=0xb7de82b4, locals=0xb7de82b4, fromlist=0x818c888, level=-1)    at Python/import.c:2076#8  0x080d8809 in builtin___import__ (self=0x0, args=0xb7d9de34, kwds=0x0) at Python/bltinmodule.c:47#9  0x0814d04b in PyCFunction_Call (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Objects/methodobject.c:77#10 0x08062974 in PyObject_Call (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Objects/abstract.c:1861#11 0x080ecad2 in PyEval_CallObjectWithKeywords (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Python/ceval.c:3446#12 0x080e7b33 in PyEval_EvalFrameEx (f=0x821bc04, throwflag=0) at Python/ceval.c:2068#13 0x080eaf9e in PyEval_EvalCodeEx (co=0xb7d9ab08, globals=0xb7de82b4, locals=0xb7de82b4, args=0x0, argcount=0, kws=0x0, kwcount=0,    defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:2840#14 0x080e013e in PyEval_EvalCode (co=0xb7d9ab08, globals=0xb7de82b4, locals=0xb7de82b4) at Python/ceval.c:494#15 0x08116ab0 in run_mod (mod=0x8220378, filename=0x81653bb "<stdin>", globals=0xb7de82b4, locals=0xb7de82b4, flags=0xbfd92f70,    arena=0x81c5cd8) at Python/pythonrun.c:1273#16 0x081151e1 in PyRun_InteractiveOneFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", flags=0xbfd92f70) at Python/pythonrun.c:792#17 0x08114e54 in PyRun_InteractiveLoopFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", flags=0xbfd92f70) at Python/pythonrun.c:723#18 0x08114cac in PyRun_AnyFileExFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", closeit=0, flags=0xbfd92f70) at Python/pythonrun.c:692#19 0x08059d60 in Py_Main (argc=1, argv=0xbfd93074) at Modules/main.c:523#20 0x08058e26 in main (argc=136033156, argv=0xb7dc37b4) at ./Modules/python.c:23
  • 可以建立module並將其加入sys.modules是在函數PyImport_ExecCodeModuleEx中完成的,此後,就會將新module的dict作為locals(globals)傳給執行A位元組碼的函數,在執行A位元組碼時,發現需要IMPORT_NAME A,這時虛擬機器會發現在sys.modules中已經存在A,所以會直接返回這個A的module,而在接下來的IMPORT_FROM時,會從這個module中試圖找到B,而此時這個module裡虛擬機器只載入了__builtin__,__file__等屬性,載入B的位元組碼還沒執行到(也不可能執行到),所以虛擬機器就會拋出無法載入B的異常。

  • 在<python源碼剖析>前言中提到這樣一個問題:

 

 [A.py]from B import Dclass C:    pass[B.py]from A import Cclass D:    pass

這裡無法載入D,這個問題是和本文一開始提出的問題相似的。

 

總結:
IMPORT_NAME位元組碼命令的執行流程如
  1. 假設需要import A,那麼虛擬機器首先在sys.modules中尋找是否已經load過A,
  2. 找到則返回該對象,命令結束;
  3. 如果沒有找到,那麼虛擬機器會建立一個module對象,
  4. 然後向module對象中添加必要的屬性(builtin等),

  5. 然後用這個module中的dict作為globals(locals)執行A,
  6. 然後返回

(轉)Python中的模組迴圈匯入問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.