我們在Python中,總是使用import來匯入另外一個模組(檔案)中的內容,如果是從Java或C轉過來的程式設計師,有幾個常見的坑要注意一下:
import也是執行語句,可以在程式碼任何部位執行。如果我們把import寫在程式碼中間,IDE很可能會有個警告,但只要邏輯正確,就不用理它:
import a if __name__ == "__main__": import b b.methodx()
類似以上程式碼,只有當前檔案作為主模組執行的時候,才匯入模組b,不但減少了浪費,而且有可能避免迴圈引用。
- import執行的時候,如果是第一次匯入,那麼會把對應模組執行一遍,這個模組裡所有的頂級程式碼都會執行,所以是個很耗時的操作,這點跟Java或者C只是獲取型別定義完全不同;所以那些被複用的模組,要儘量減少在頂級程式碼塊中實現邏輯。
即使在同一個模組裡,程式碼定義的順序也很重要,被引用的程式碼一定要在引用的前面定義,比如:
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
來解決,這是另外一個問題了。- import的namespace也很重要,名稱空間不同,會被認為是不同的模組。而一個模組,用絕對路徑或者相對路徑引入,會被識別為不同的名稱空間,比如下面的package結構:
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更改。