2017-10-05 202 views
1

我正在使用Python 2.7。Python子模塊:「導入爲」與「導入」

這是我的文件夾結構。 (temp已添加到此係統路徑中。)

temp 
| 
|--main.py 
| 
|--sub 
    | 
    |--__init__.py 
    | 
    |--sub2 
     | 
     |--__init__.py 
     | 
     |--square.py 

文件內容如下所示。

main.py: 

import sub.sub2 as sub2 
sub2.run() 
sub/__init__.py: empty 
sub/sub2/__init__.py: 

import sub.sub2.square as square 
def run(): 
    square.square_it(3) 
sub/sub2/square.py: 

def square_it(x): return x**2 

當我執行main.py,我收到以下錯誤(忽略行號):

Traceback (most recent call last): 
    File "main.py", line 3, in <module> 
    import sub.sub2 as sub2 
    File "/home/gimlisonofgloin1/temp/sub/sub2/__init__.py", line 3, in <module> 
    import sub.sub2.square as square 
AttributeError: 'module' object has no attribute 'sub2' 

,我可以通過改變發生於任何這些語句的錯誤的語句解決這個問題(在最後三級所列的解決方案,我要適當地改變函數調用,當然):

  • from sub.sub2 import square as square;
  • from sub.sub2.square import square_it;
  • from .square import square_it(正如用戶NeErAj KuMaR的回答中所指出的那樣);或
  • import sub.sub2.square

我的問題是:爲什麼原來的行代碼產量錯誤的,即使它是語義上等同於工作(「固定」)的代碼行(特別是第一和第四列出的解決方案) ?

在試圖回答這個問題,我已經越過該位文本跌跌撞撞從Python 2.0 Reference Manual

爲了避免混淆,你不能導入子模塊「作爲」一個不同的本地名稱。所以'import module as m'是合法的,但'import module.submod as s'不是。後者應該寫成'從模塊導入子模式爲s',見下文。

這與我收到的錯誤一致。然而,這個(看似重要的)小小的街區並不存在於Python 2.7 Reference Manual的任何地方。那麼Python 2.0引用中的小小事件仍然適用於Python 2.7嗎?或者我得到這個錯誤的原因完全不同,我不知道?

+0

您錯過了臨時目錄中的__init__py。 – Dharmesh

+0

哎呦。我的意思是說'temp'已經在Python路徑中,這意味着''temp''不需要'__init __。py'文件。編輯的問題來反映這一點。 –

回答

0

我想你偶然到這似乎是一個fixed in Python 3.7 Alpha 1 thanks to Serhiy Storchaka鮮爲人知的Python的「疑難雜症」。檢查這個問題:http://stackoverflow.com/questions/41845671/import-as-in-python-3

這在Python Ideas中討論過,但import foo.bar as eggsfrom foo import bar as eggs不一樣。他們產生不一致的字節碼。

根本原因是導入週期。我與DIS玩了一圈,發現以下(我懷疑別人已經發現了這一點,但線程很難最初跟着我):

>>> dis.dis('import a.b') 
1   0 LOAD_CONST    0 (0) 
      2 LOAD_CONST    1 (None) 
      4 IMPORT_NAME    0 (a.b) 
      6 STORE_NAME    1 (a) 
      8 LOAD_CONST    1 (None) 
     10 RETURN_VALUE 
>>> 

相比

>>> dis.dis('import a.b as c') 
1   0 LOAD_CONST    0 (0) 
      2 LOAD_CONST    1 (None) 
      4 IMPORT_NAME    0 (a.b) 
      6 LOAD_ATTR    1 (b)  <-- error here 
      8 STORE_NAME    2 (c) 
     10 LOAD_CONST    1 (None) 
     12 RETURN_VALUE 
>>> 

這顯示的是執行「導入ab」和「導入ab作爲c」是不同的。前者調用導入('a.b',...),它返回模塊'a'並將其存儲在變量'a'中。在OP的情況下,由於導入週期的存在,當sys.modules ['a.b']存在時,模塊'a'還沒有屬性'b'。這就是在後面的例子中,LOAD_ATTR操作碼失敗的原因。

>>> dis("import sys.path as path") 
1   0 LOAD_CONST    0 (0) 
      3 LOAD_CONST    1 (None) 
      6 IMPORT_NAME    0 (sys.path) 
      9 LOAD_ATTR    1 (path) 
     12 STORE_NAME    1 (path) 
     15 LOAD_CONST    1 (None) 
     18 RETURN_VALUE 

對於「進口的sys.path爲路徑」,在給定的模塊名稱是「sys.path中」,並在堆棧上的「從列表」項是無。在它甚至到達LOAD_ATTR行之前,它會失敗,因爲「sys.path」不是可導入的模塊。因此將LOAD_ATTR更改爲IMPORT_FROM對其行爲沒有影響。

>>> dis("from sys import path") 
1   0 LOAD_CONST    0 (0) 
      3 LOAD_CONST    1 (('path',)) 
      6 IMPORT_NAME    0 (sys) 
      9 IMPORT_FROM    1 (path) 
     12 STORE_NAME    1 (path) 
     15 POP_TOP 
     16 LOAD_CONST    2 (None) 
     19 RETURN_VALUE 

對於「從SYS導入路徑」,在給定的模塊名稱是「SYS」,並在堆棧上的「從列表」項包含字符串「路徑」的元組。這是有效的,因爲「sys」是可導入的它有一個「路徑」屬性。

我想你必須等待3.7或更改導入。

0

更換的SUB2 __init__.py代碼代碼如下

sub/sub2/__init__.py: 

from .square import square_it 
def run(): 
    square_it(3) 
+0

我知道這(明確的相對導入)可以完成,但我沒有列出它。但是,我的問題是_why_特定的聲明「import sub.sub2.square as square」會導致錯誤。我會列舉這是另一個成功的替代方案。 –

+0

看看這個拉請求:https://bugs.python.org/issue30024 –

+0

@PauloScardine,謝謝!這解釋了一切。關於拉請求的評論鏈接到一個StackOverflow問題/答案,我認爲它幾乎與我的相同。我是否應該回答自己在問題上的輕微變化並鏈接到該答案,或者將我的問題標記爲重複? –