sys.path源碼分析
先說sys.path初始化結果:
*.pth定義的egg目錄放在最前面;
PYTHONPATH隨後(如果定義了PYTHONPATH環境變數的話);
pythonx.x.zip所在目錄隨後;
prefix plat_linux2 lib_tk lib_old這些目錄隨後;
lib-dynload的目錄隨後放進去;
*.pth定義的其他目錄放在最後;
預留了sitecustomize.py和usercustomize.py用戶用戶自定義一些行為。
這些path有兩個模塊構成:
getpath.c的caculate_path計算module_search_path、prefix、exec_prefix全局變數;
site.py用於將site-package目錄本身和該目錄下定義的*.pth文件中定義的目錄(包括egg,其實egg也是一個目錄,一個壓縮的目錄)放入sys.path中。
module_search_path、prefix、exec_prefix怎麼來的?
都是由caculate_path這個函數計算出來。
首先計算出python可執行文件所在的目錄,如果當前的可執行文件是一個軟鏈接,裡面會通過readlink找到最終的真正的可執行文件所在的目錄,這個目錄被賦值給變數argv0_path;
然後通過search_for_prefix從argv0_path一層層的上去找到包含landmark=lib/python2.7/os.py的目錄,並將該目錄(不包含os.py)賦值給prefix。
將prefix向上退兩層,然後加上python27.zip,將其賦值給局部變數zip_path。
同計算prefix相同的邏輯,計算exec_prefix,只不過這時候landmark變成了lib/python2.7/lib-dynload。並賦值給exec_prefix。(getpath.c的注釋中說道prefix放一些跨平台的代碼;而exec_prefix放平台先關的代碼或者二進位文件)。
然後就開始組合module_search_path了:
如果定義了PYTHONPATH,放進去;
zip_path放進去;
通過prefix+defpath(=:plat_linux2:lib_tk:lib_old)找到這些目錄的全路徑放進去,由於第一個是空的,所以會把prefix也放進去;
把exec_prefix放進去。
site-package怎麼放進去的?
site.py中,有個addsitedir函數,該函數將讀取site-package中的pth文件內容,並將該內容一行行的加入sys.path中,如果該行以import開頭,則執行。
而將egg放在sys.path的前面就是pth文件中,用import開頭來做的。
計算出來的三個全局變數什麼時候加入到sys.path中?
在pythonrun.c中的Py_InitializeEx函數中:
首先通過_PySys_Init初始化sys模塊,此時已經計算出了三個變數,並將prefix、exec_prefix賦值給sys模塊的prefix和exec_prefix;
後面有調用PySys_SetPath將module_search_path賦值給sys模塊的path;
在然後通過initsite初始化site.py模塊,由於該模塊里有main的調用,因此import site模塊會自動計算site-package相關環境,並進一步加入sys.path中。
dist-package、site-package有什麼區別?
dist-package可能存在兩個:
/usr/lib/pythonx.x/dist-packages。利用debian包管理器安裝的python第三方庫會放入這裡;
/usr/local/lib/pythonx.x/dist-packages。由於pip、easy_install這些工具也是用debian包管理安裝的,所以通過這些工具安裝的第三方庫放在這裡;
site-package只存在與自己源碼編譯的python版本中:
比如在我的電腦上由於用了默認路徑,就存在/usr/local/lib/pythonx.x/site-packages/。通過自己編譯的python安裝的第三方庫都放在該目錄中。
搞這些是因為做好兼容,防止第三方庫由於python版本不同而導致的不兼容。因此,如果你用系統自帶的python安裝了很多第三庫,然後又自己編譯了python,就要重新安裝這些第三方庫。可以通過一些技巧公共部分第三方庫(比如修改pth),但是不建議這樣搞,因此這樣做並不能保證兼容性。
如何修改sys.path?
修改syspath 有幾種方式:
直接加入sys.path列表中;
將目錄加入PYTHONPATH環境變數中,這種方式簡單粗暴,容易引起不可預知的問題,比如其他地方也用到了這個環境變數;
將第三方庫egg目錄或者第三放庫py目錄加入.pth中
自定義sitecustomize.py和usercustomize.py文件。
參考:
http://mikeboers.com/blog/2014/05/23/where-does-the-sys-path-start
https://stackoverflow.com/questions/9387928/whats-the-difference-between-dist-packages-and-site-packages
TAG:LynnTech |