import聲明即用於導入模塊,比如import numpy as np
,但是涉及複雜工程目錄時容易搞得稀裏糊塗,於是我專門使用了python3.7
來測試並解決import相關問題。
基本定義
-
module:即模塊,也就是中各種
.py
文件,模塊名就是文件名 -
built-in-module:即內置模塊,就是在安裝python的時候系統編譯在python解釋器中的,比如
numpy
-
package:任何包含
__init__.py
文件的文件夾就是一個package。注意根據python document,在python3.3以上中,即使沒有__init__.py
,文件夾也被自動視作一個package -
object:即對象,在python中,對象可以是函數,類以及變量等。
import與sys.path
如果import導入了一個module,就能運行該module中的所有代碼。導入package時,會運行package目錄下的__init__.py
,然後通過__init__.py
運行package目錄下的所有module,__init__.py
可以是空的,前面講到在python3.3以上,__init__.py
可以沒有。
import會自動在sys.path
包含的目錄中尋找相應的模塊或者包,在一個.py
腳本被運行時,sys.path
會初始化包含以下目錄:
PYTHONPATH
,即常說的系統環境變量PATH
- 默認安裝的模塊目錄,比如
numpy
就在此目錄中 .py
腳本所在的目錄,這一點根據腳本所在的目錄不同是可以變化的。
常用的import方式
常用的import方式有四種:
import <package>
import <module>
from <package> import <module or subpackage or object>
from <module> import <object>
當然還有
import <module> as # 比如 import numpy as np
import <object> as
場景1:導入系統以及同級目錄下的模塊
對於以下的工程目錄:
test/ # 跟目錄
packA/ # package packA
subA/ # subpackage subA
__init__.py
sa1.py
sa2.py
__init__.py
a1.py
a2.py
packB/ # package packB (implicit namespace package)
b1.py
b2.py
other.py
start.py
要在start.py
中導入系統numpy
模塊,以及同級目錄下的other
模塊,只需要:
import numpy
import numpy as np # 通常將numpy重命名爲np
import other
然後運行start.py
腳本
場景2:導入子目錄下的模塊
如果需要在start.py
中導入a1.py
,b1.py
以及sa1.py
模塊,只需要在start.py
中:
import packA.a1
import packB.b1
import packA.subA.sa1
如果只需要導入比如a1.py
某個函數a1_func()
,只需要:
from packA.a1 import a1_func()
from packA.subA.sa1 import sa1_func() # 跟上面同理
注意在start.py
和在a1.py
導入sa1.py
是不同的,在a1.py
需要:
import subA.sa1
from subA.sa1 import sa1_func()
from subA import sa2
因爲運行a1.py
時sys.path
的相應目錄已經改變,只包含a1.py
腳本所在的目錄。而對於python3,在start.py
是不能跨越子目錄直接導入sa1.py
的,比如from subA import sa1
,但是在python2中可以,下面做個簡單的總結:
運行 | from packA.subA import sa1 | from subA import sa1 |
---|---|---|
start.py | OK | Py2 OK, Py3 fail (subA not in test/) |
a1.py | fail (packA not in test/packA/) | OK |
場景3:導入父目錄下的模塊
如果需要在a1.py
中導入父目錄下other.py
或者是packB
目錄下的b1.py
,此時就需要對sys.path
作出修改了,因爲運行a1.py
時sys.path
包含a1.py
所在的目錄packA
,並不包含目錄packB
以及父目錄test
。
修改sys.path
需要用到sys
以及os
模塊,首先需要知道怎麼獲取當前目錄以及父目錄,在a1.py
中如下:
current_path=os.path.dirname(__file__) #當前a1.py所在的目錄packA
parent_path=os.path.dirname(os.path.dirname(__file__)) #當前a1.py所在的父目錄或者說上級目錄test
p_parent_path=os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ##獲取上上級目錄
下面修改sys.path
,對於在a1.py
中導入父目錄下other.py
或者是packB
目錄下的b1.py
,只需要將parent_path
加入到```sys.path``即可,
sys.path.append(parent_path)
然後就可以導入other.py
以及b1.py
了,完整代碼如下:
import sys
import os
# print(os.getcwd())
# sys.path.append(os.getcwd())
parent_path=os.path.dirname(os.path.dirname(__file__)) # 獲取上級目錄
p_parent_path=os.path.dirname(os.path.dirname(os.path.dirname(__file__))) # 獲取上上級目錄
sys.path.append(parent_path) # 修改sys.path
import other # 導入test下的other
import packB.b1 # 導入b1
import packA.a2 #導入a2
import a2 # 此時sys.path既包含上級目錄test也包含當前目錄packA,所以跟上面一樣
此外順便提到os.getcwd()
,注意該函數是獲取當前終端的路徑而不是腳本的路徑,所以爲了避免混淆,建議採用os.path.dirname(__file__)
這樣的形式。
要點提煉
import
最重要的是看sys.path
所包含的目錄,當我們運行腳本時sys.path
會自動包含運行腳本所在的目錄- python3中不能越級導入,如果
sys.path
只包含test
所在目錄,導入sa1.py
時,只能import packA.subA.sa1
,而不能import subA.sa1
,也不能import sa1
- 接第二點,如果
sys.path
經過修改後,既包含test
所在目錄,也包含packA
所在目錄,那麼import packA.subA.sa1
和import subA.sa1
都可以,如果再包含sa1
所在目錄,那麼import sa1
也可以 - 第三點可能會造成困惑,一種建議的習慣就是在每個運行的腳本中加入工程的根目錄即
test
所在目錄到sys.path
中,然後所有導入按照import packA.subA.sa1
的形式從第一級目錄追溯到要導入的模塊即可,而不是一會import packA.subA.sa1
,一會import subA.sa1
陷入混淆,只導入當前目錄所在的模塊依然可以import sa1
的形式。