工作目錄
文件目錄:文件所在的目錄
工作目錄:執行python命令所在的目錄
D:.
| main.py
|
+---data
| data.txt
|
+---model
| | model.py
| | train.py
| | __init__.py
| |
| +---nlp
| | | bert.py
|
\---util| view.py| __init__.py
我們以上圖為例,很容易知道main.py
的文件目錄是D:/test/main.py
,而train.py
的文件目錄是D:/test/model/train.py
。
但是工作目錄是哪個呢?我們打開vscode,可以看到terminal中也顯示了一個目錄D:\test
,也就是我們項目的目錄,我們點一下運行,執行了一個python腳本,輸出一個PS D:\test> python -u "d:/test/model/train.py"
在哪里執行的這個腳本呢?D:\test
,這就是我們的【工作目錄】
工作目錄可以是任何目錄,只要能通過python運行就行,例如我們打開一個終端執行我們的train.py
腳本PS C:\Users\xxx> python D:\test\model\train.py
,那么工作目錄就變成了C:\Users\xxx
。
讀文件
python執行的時候,open函數中相對路徑的會以當前的工作目錄為基準。例如我們再vscode運行腳本,工作目錄是D:\test
,那么就可以直接訪問test目錄下的所有文件以及所有子目錄和子目錄下的文件。
如果我們想在main.py訪問data中的data.txt文件,很自然可以寫出如下代碼
with open('data/data.txt') as f:res = f.read()print(res)
但如果我們想在train.py中訪問data.txt目錄怎么辦呢?你可能會用相對路徑寫出如下的代碼
with open('../data/data.txt') as f:res = f.read()print(res)
但是會報如下錯誤
Traceback (most recent call last):
File “d:/test/model/train.py”, line 1, in
with open(‘…/data/data.txt’) as f:
FileNotFoundError: [Errno 2] No such file or directory: ‘…/data/data.txt’
這是因為我們執行train.py
文件的目錄D:\test
,python會以工作目錄為基準目錄,即D:\test
,相對路徑是相對于工作目錄的,因此實際訪問的就是D:\test/../data/data.txt
。當然就找不到了。正確的寫法就是
with open('data/data.txt') as f:res = f.read()print(res)
寫文件
我們訓練了一個模型,想要把最終的結果保存在model目錄中,該怎么辦呢?工作目錄依然是D:\test
。下面這種寫法依然是錯誤的
with open('tf.pb','w') as f:f.write('hello world')
一定要明白相對路徑是相對于工作目錄來說的,我們執行命令的路徑是D:\test
,因此最終寫入的地方就是D:\test\tf.pb
,而不是model文件夾,正確的寫法如下
with open('model/tf.pb','w') as f:f.write('hello world')
切換工作目錄
如果我們把工作目錄換掉了,上面的路徑就都不對了,例如在C:\users\xxx
目錄下執行python命令,那么上面的路徑就就變成了C:\users\xxx\data\data.txt
或者C:\users\xxx\model\tf.pb
了,非常的不好用。
- 查看當前工作目錄
os.getcwd()
- 切換工作目錄 os.chdir(path)
修改train.py和main.py為
import os
cur_dir = os.getcwd()
print(cur_dir)
然后我們打開個終端執行兩個腳本
PS C:\Users\xxx> python d:/test/model/train.py
train.py work dir = C:\Users\xxx
PS C:\Users\xxx> python d:/test/main.py
main.py work dir = C:\Users\xxx
發現他們的工作目錄都是一樣的,重新修改main.py和train.py,并且切換工作目錄
# mian.py
import os
cur_dir = os.getcwd()
print("old work dir = ", cur_dir)
os.chdir(r'D:\\test')
cur_dir = os.getcwd()
print("new work dir = ", cur_dir)
with open('data/data.txt') as f:res = f.read()print(res)
# train.py
import os
cur_dir = os.getcwd()
print("old work dir = ", cur_dir)
os.chdir(r'D:\\test')
cur_dir = os.getcwd()
print("new work dir = ", cur_dir)
with open('model/tf.pb','w') as f:f.write('hello world')
PS C:\Users\xxx> python d:/test/main.py
main.py old work dir = C:\Users\xxx
main.py new work dir = D:\test
hello world
絕對路徑
我個人喜歡以執行文件的路徑作為相對目錄的基準。例如執行main.py文件的時候,喜歡以main.py所在的目錄D:\test
為基準,執行train.py的時候,喜歡以train.py所在的目錄D:\test\model
為基準,這個時候我們可以先獲取文件的絕對路徑,然后根據這個路徑拼接出我們想要的路徑來, os.path.abspath(__file__)
可以獲取文件的絕對路徑,
# main.py
import os
file_path = os.path.abspath(__file__)
file_dir = os.path.dirname(file_path)
print('file_dir=',file_dir)print("cur work dir = ", os.getcwd())data_path = os.path.join(file_dir, 'data/data.txt')
print('data_path=',data_path)
with open(data_path,'r') as f:res = f.read()print(res)
同樣
# train.py
import os
file_path = os.path.abspath(__file__)
file_dir = os.path.dirname(file_path)
print('file_dir=',file_dir)print("cur work dir = ", os.getcwd())
#
model_path = os.path.join(file_dir, 'tf.pb')
print('model_path=',model_path)
with open(model_path,'w') as f:f.write('hello world')
PS C:\Users\xxx> python d:/test/model/train.py
file_dir= d:\test\model
cur work dir = C:\Users\xxx
model_path= d:\test\model\tf.pb
很直觀了,我們根據文件的目錄拼接出我們想要的目錄,然后執行讀寫操作。
導入自定義包/文件
我們util目錄中有一個view.py的文件,里面包含了一些工具類和方法
# view.py
class View:def __init__(self, x, y):self.x = xself.y = y
我們想要在train.py中調用該怎么辦呢?
import util.view import View
我們以D:\test
為工作目錄,執行train.py文件,嘿嘿
Traceback (most recent call last):
File “d:/test/model/train.py”, line 1, in
from util.view import View
ModuleNotFoundError: No module named ‘util’
這是因為python在導入包的時候import xxx
是以文件路徑為基準的,即以train.py為基準,發現train.py的目錄中并沒有util子目錄,因此導入失敗。聰明的你做了一個簡單的修改
import ..util.view import View
發現報了另一個錯誤
Traceback (most recent call last):
File “d:/test/model/train.py”, line 1, in
from …util.view import View
ValueError: attempted relative import beyond top-level package
這是因為python規定頂層模塊不能作為package,我們執行的是train.py文件,那么train.py所在的目錄就是頂層模塊了,train.py相對導入的的目錄不在超出了train.py所在目錄,因此導入失敗。換句話說就是相對導入只能導入同一個package下的子package或module。
# main.py
import model.train
#train.py
import util.view import View
這種方式依然會報錯,雖然我們執行的時main.py文件,但是導入model.train的時候依然會執行train.py文件。
總結一下:
- 相對路徑導入時是以當前文件為基準的,當前文件所在的目錄就是頂層模塊目錄
- 相對導入模塊不能超出頂層目錄,意味者相對導入只能導入當前文件所在目錄下的模塊或者子package
非常的疑惑,什么時候可以使用
..
呢
包查詢路徑
相對路徑只能導入同一package下的module。要導入不同package下的module可以使用絕對路徑。這就非常奇怪了,我們安裝的三方庫為什么可以在任意文件導入呢?這就不得不說python的庫查詢機制,python回去默認的一些地方查找安裝的package,具體可以通過sys.path來查看
import sysfor path in sys.path:print(path)
d:\test\model
D:\soft\python3\python37.zip
D:\soft\python3\DLLs
D:\soft\python3\lib
D:\soft\python3
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages\win32
C:\Users\x\xxAppData\Roaming\Python\Python37\site-packages\win32\lib
C:\Users\x\xxAppData\Roaming\Python\Python37\site-packages\Pythonwin
D:\soft\python3\lib\site-packages
可以看到除了第一條路徑,其余都是跟python的默認安裝路徑有關。python會按順序從這些路徑里面查找我們安裝的package,越靠前的優先級越高。例如我們導入import os
這個庫優先從項目路徑d:\test
查找os的庫,沒有找到就接著從第二個路徑查找,一直找到D:\soft\python3\Lib
發現有os的庫,然后導入。如果我們自己的項目中有一個os的庫,就會優先使用項目中的,替換掉lib里面的。
sys.path絕對路徑導入自己的包
我們把自己的package也加入到sys.path
中,python就可以找到了,一種簡單的方法就是
#train.py
import os
import sys
cur_dir = os.path.dirname(os.path.abspath(__file__))# 直接導入會找不到模塊
#import util.view# util模塊在test目錄下,我們把test目錄添加到sys.path中
sys.path.append(os.path.dirname(cur_dir))for path in sys.path:print(path)import util.view# nlp在model下面,sys.path的第一個路徑就是d:\test\model
# 可以在d:\test\model下面發現有一個nlp的package,導入成功
from nlp.bert import BERT
sys.path相對路徑導入自己的包
sys.path添加相對路徑的時候,以哪個目錄為基準目錄呢?答案是【工作目錄】,我們是在d:\test
目錄下執行的命令train.py,所以工作目錄就是d:\test
,而util
包就是在d:\test
下面的,所以我們
#train.py
import sys
sys.path.append(".")
for path in sys.path:print(path)
import util.view
忽略python自己的目錄,我們可以看到當前有sys.path有兩個目錄,第一個是d:\test\model
,也就是【文件路徑】,python會自動把執行的文件的路徑加進來,所以文件目錄下的所有package和module都可以通過相對目錄的方式找到。第二個是一個.
,這個就是工作目錄。因為我們使用的是相對路徑導入,而相對路徑的基準目錄就是d:\test
,.
代表的就是基準目錄。
d:\test\model
.
使用絕對路徑打印更直觀一些
# train.py
import sys
import os
sys.path.append(".")
for path in sys.path:print(os.path.abspath(path))
import util.view
d:\test\model
d:\test
d:\test
添加到了sys.path
中,因此d:\test
下面的package就都可以訪問到了。很明顯相對路徑很不好用,當我們把工作目錄切換到C:\users\xxx
的時候,就又找不到了
PS C:\Users\xxx> python d:/test/model/train.py
d:\test\model
D:\soft\python3\python37.zip
D:\soft\python3\DLLs
D:\soft\python3\lib
D:\soft\python3
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages\win32
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages\win32\lib
C:\Users\xxx\AppData\Roaming\Python\Python37\site-packages\Pythonwin
D:\soft\python3\lib\site-packages
C:\Users\xxx
Traceback (most recent call last):
File “d:/test/model/train.py”, line 6, in
import util.view
ModuleNotFoundError: No module named ‘util’
所以我們最好使用絕對路徑來添加
小結
核心訴求就是找到package,為什么會找不到我們自己定義的package呢?因為沒有把路徑添加到python的包搜索路徑里面,怎么添加呢?使用sys.path.append
添加。定位到package的絕對路徑,然后添加進去。