自己編寫的python代碼經常需要分模塊文件以及包,梳理一下調用順序、執行順序、工作路徑、函數與變量等
工作路徑
首先是工作路徑,當模塊代碼放在統一的包內的時候,其路徑和外層的包路徑不同,當作為主調用時,工作路徑是該模塊所在的路徑,而作為模塊import進時,工作路徑是主調用的路徑
這是PyTest/utils/utils.py文件,輸出路徑,並定義了一個輸出路徑函數
1 import sys 2
3 def printpath(): 4 print('function') 5 print(sys.path[0]) 6 print('function end') 7
8 9
10 print('utils main') 11 print(sys.path[0]) 12 print('utils main end')
在該目錄下運行它,得到的是:
1 zry@seupalm:~/PyTest/utils$ python utils.py 2 utils main 3 /home/zry/PyTest/utils 4 utils main end
毫無疑問,路徑就是當前路徑。
在外層的文件PyTest/fun.py:
1 import sys 2 from utils.utils import *
3
4 print('fun main') 5 print(sys.path[0]) 6 printpath() 7 8 print('fun main end')
這里該代碼調用了utils/utils.py的內容,運行結果:
1 zry@seupalm:~/PyTest$ python fun.py 2 utils main 3 /home/zry/PyTest 4 utils main end 5 fun main 6 /home/zry/PyTest 7 function
8 /home/zry/PyTest 9 function end 10 fun main end
可以發現,所有的路徑輸出都是主調用的路徑了。因此需要注意的其實是,在工具包的模塊里,考慮避免設置固定路徑,因為作為模塊被調用時,並不確定工作路徑是什么
執行順序
此外,還可以發現import時utils.py先於fun.py的內容運行,因此,在import時其實是將utils.py這個模塊運行了一遍的,如果僅希望引入函數,而不希望運行代碼,則可以在不希望運行的代碼前加上if __name__ == '__main__':
即僅在作為主調用時才運行,注意此處name和main兩邊都是兩個下划線
修改PyTest/utils/utils.py:
1 import sys 2
3 def printpath(): 4 print('function') 5 print(sys.path[0]) 6 print('function end') 7
8 if __name__ == '__main__': 9 print('utils main') 10 print(sys.path[0]) 11 print('utils main end')
此時重新運行PyTest/fun.py:
1 zry@seupalm:~/PyTest$ python fun.py 2 fun main 3 /home/zry/PyTest 4 function
5 /home/zry/PyTest 6 function end 7 fun main end
就不會運行utils.py的主函數部分了
模塊內變量
如果沒有加上判斷__name__的語句,實際是在import時就會運行一遍,所以對於from utils.utils import *這種操作同樣會將模塊里的變量也引入進來
仍然修改PyTest/utils/utils.py:
1 import sys 2
3 def printpath(): 4 print('function') 5 print(sys.path[0]) 6 print('function end') 7
8 a = 'hello world'
9 print('utils main') 10 print(sys.path[0]) 11 print('utils main end')
並在fun.py中輸出a得到:
1 zry@seupalm:~/PyTest$ python fun.py 2 utils main 3 /home/zry/PyTest 4 utils main end 5 fun main 6 /home/zry/PyTest 7 function
8 /home/zry/PyTest 9 function end 10 hello world 11 fun main end
可以看到hello world被輸出了。
引入指定函數方法
但如果是僅引入一個函數方法呢,即使用from utils/utils import printpath:
修改PyTest/fun.py:
1 import sys 2 from utils.utils import printpath 3
4 print('fun main') 5 print(sys.path[0]) 6 printpath() 7 print(a) 8 print('fun main end')
運行:
1 zry@seupalm:~/PyTest$ python fun.py 2 utils main 3 /home/zry/PyTest 4 utils main end 5 fun main 6 /home/zry/PyTest 7 function
8 /home/zry/PyTest 9 function end 10 Traceback (most recent call last): 11 File "fun.py", line 7, in <module>
12 print(a) 13 NameError: name 'a' is not defined
可以看到PyTest/utils/utils.py仍然被運行了一遍,但是過程中的變量則並不會被引入,因為只引入了指定的函數方法。
跨級調用
當需要跨級import時,如果是主調用直接import進來,則並不需要擔心
編寫PyTest/utils/src/uu.py
1 import sys 2
3 def uufun(): 4 print('uufun') 5 print(sys.path[0]) 6 print('uufun end')
PyTest/fun.py
1 import sys 2 from utils.src.uu import *
3
4 print('fun main') 5 uufun() 6 print('fun main end')
運行fun.py
1 zry@seupalm:~/PyTest$ python fun.py 2 fun main 3 uufun 4 /home/zry/PyTest 5 uufun end 6 fun main end
可以看出是成功調用了的
但如果希望用PyTest/utils/utils.py調用PyTest/utils/src/uu.py,再用PyTest/fun.py調用PyTest/utils/utils.py,則會出現問題
修改PyTest/utils/utils.py,增加一行:
1 from src.uu import uufun
此時運行PyTest/utils/utils.py仍然沒有問題
但修改PyTest/fun.py來調用PyTest/utils/utils.py,import改為:
1 from utils.utils import *
或
1 from utils.utils import printpath
運行結果都會出現報錯
1 zry@seupalm:~/PyTest$ python fun.py 2 Traceback (most recent call last): 3 File "fun.py", line 2, in <module>
4 from utils.utils import printpath 5 File "/home/zry/PyTest/utils/utils.py", line 2, in <module>
6 from src.uu import uufun 7 ModuleNotFoundError: No module named 'src'
此時很顯然就是import語句的路徑也是根據主調用得來的,所以在二級調用時路徑也會出現問題
如果用比較暴力的解決辦法,就將PyTest/utils/utils.py里import的路徑變為utils.src.uu,這樣就能夠二級調用了,而如果是import *的形式,則會將PyTest/utils/utils.py里import得到的PyTest/utils/src/uu.py內函數一並引入主調用。
但如果遇到不希望改動內層的代碼,畢竟對於內層來說,文件目錄層級並不是這樣的,那么可以怎么辦呢?
由於開發經歷仍然有限,我采取的是如下方法:
此時PyTest是主目錄,PyTest/utils是一級目錄,PyTest/utils/src是二級目錄,首先在一級目錄下放置一個__init__.py空文件,這是為了讓主目錄下的代碼import一級目錄的內容時,可以將整個一級目錄視為一個包,當然import一級目錄的內容時首先會運行__init__.py,此處因為是空文件,所以只是讓一級目錄能夠作為包被識別。
然后在主目錄下的函數調用一級目錄下的內容時,將一級目錄的路徑加入系統路徑,這樣就不用修改一級目錄內部的東西了。
具體是將PyTest/fun.py進行如下修改:
1 import sys 2 sys.path.append('utils') 3 from utils.utils import *
4
5 print('fun main') 6 uufun() 7 print('fun main end')
此時就能夠正常運行,並通過一級目錄的模塊調用到二級目錄的內容了。當然,添加路徑的語句也可以放在一級目錄的__init__.py里,畢竟運行順序是一樣的。
不過如果后續開發又把主目錄當成模塊,進行了三級調用呢……這樣utils這個路徑也是相對路徑,是不是仍然需要修改此時的一級目錄內容呢……emmmm沒有測試過,我覺得還是好好設計好項目結構比較好23333