博客 / 詳情

返回

Python import 時要注意的幾個問題

我們在Python中,總是使用import來導入另外一個模塊(文件)中的內容,如果是從Java或C轉過來的程序員,有幾個常見的坑要注意一下:

  1. import也是執行語句,可以在代碼任何部位執行。如果我們把import寫在代碼中間,IDE很可能會有個警告,但只要邏輯正確,就不用理它:

    import a
    
    if __name__ == "__main__":
     import b
     b.methodx()

    類似以上代碼,只有當前文件作為主模塊運行的時候,才導入模塊b,不但減少了浪費,而且有可能避免循環引用。

  2. import執行的時候,如果是第一次導入,那麼會把對應模塊執行一遍,這個模塊裏所有的頂級代碼都會執行,所以是個很耗時的操作,這點跟Java或者C只是獲取類型定義完全不同;所以那些被複用的模塊,要儘量減少在頂級代碼塊中實現邏輯
  3. 即使在同一個模塊裏,代碼定義的順序也很重要,被引用的代碼一定要在引用的前面定義,比如:

    class A:
     x: int = 5
    
    
    class B:
     def __init__(self, y: A):
         self.y = y
    
    
    if __name__ == "__main__":
     a = A()
     b = B(a)
     print(b.y.x)
    

    上面這段代碼裏就不能把class A的定義放在class B後面,否則運行時會出錯,提示name 'A' is not defined;當然如果僅僅是annotation裏引用的話,可以通過from __future__ import annotations來解決,這是另外一個問題了。


下面的內容是有表述錯誤的,但這個錯誤有些典型,還是保留下來,最下方會解釋:

  1. import的namespace也很重要,命名空間不同,會被認為是不同的模塊。而一個模塊, 用絕對路徑或者相對路徑引入,會被識別為不同的命名空間 ,比如下面的package結構:

test import

module_a的內容如下:

a = {"value": 15}
print(f'a={a} in module_a')

module_b會用絕對路徑引用module_a

from lang.test_import.module_a import a


a['value'] = a['value'] + 1

print(f'a={a} in module_b')

module_c1和module_c2分別用相對路徑和絕對路徑引入module_a,再引入module_b,就會得到不同的結果:

from module_a import a
import module_b

print(f'a={a} in module_c1')
a={'value': 15} in module_a
a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 15} in module_c1
from lang.test_import.module_a import a
import module_b

print(f'a={a} in module_c2')
a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 16} in module_c2

可以看到在module_c1中,module_a被引入了兩次,認作不同的模塊,從而也引入了兩個變量a,而c1模塊,只承認自己引入的相對路徑命名空間中的a,所以這裏的a.value,並沒有被模塊b更改。


上面的表述中,關鍵錯誤在於:把同一路徑下的絕對路徑當做了相對路徑,真正的相對路徑引入,必須是.或者..開頭的,而上面之所以寫作from module_a import a的形式,是因為直接運行python文件時,解釋器找不到根package環境,只能把自己當做根;而如果它是被其他模塊
import的,就會基於那個模塊的根來判斷命名空間;

如果一定要基於main方法運行,可以強行指定所屬package:

__package__ = lang.test_import
from .module_a import a
import module_b

print(f'a={a} in module_c1')

這時的輸出結果,就跟絕對路徑一樣了:

a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 16} in module_c2
  • 關於import和相對路徑的含義和解釋,我見到最清晰的中文文檔是這篇:Python之模塊詳細梳理(四),關於相對導入報錯最優雅解決方法
user avatar yutou_5c10e66caa840 頭像 cyoahs 頭像 u_16099352 頭像 u_16871111 頭像 u_15654855 頭像
5 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.