參考:
sys.path
PYTHONPATH
Python sys.path詳細(xì)介紹
環(huán)境
- Mac
- Python 3.7.4
著急的小伙伴可翻到最后看總結(jié)。
sys.path
A list of strings that specifies the search path for modules. Initialized from the environment variable
PYTHONPATH, plus an installation-dependent default.
As initialized upon program startup, the first item of this list,
path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input),path[0]is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result ofPYTHONPATH.
A program is free to modify this list for its own purposes. Only strings and bytes should be added to
sys.path; all other data types are ignored during import.
- 指定模塊搜索路徑的字符串列表。從環(huán)境變量PYTHONPATH初始化,加上與安裝相關(guān)的默認(rèn)值。
- 在程序啟動(dòng)時(shí)初始化,這個(gè)列表的第一項(xiàng)path[0]是包含用于調(diào)用Python解釋器的腳本的目錄。如果腳本目錄不可用(例如,解釋器是交互式調(diào)用的,或者腳本是從標(biāo)準(zhǔn)輸入中讀取的),路徑[0]是空字符串,它指示Python首先搜索當(dāng)前目錄中的模塊。注意,腳本目錄是在PYTHONPATH插入條目之前插入的。
- 程序可以根據(jù)自己的目的自由地修改這個(gè)列表。只需要向sys.path添加字符串和字節(jié);導(dǎo)入期間將忽略所有其他數(shù)據(jù)類型。
PYTHONPATH
Augment the default search path for module files. The format is the same as the shell’s
PATH: one or more directory pathnames separated byos.pathsep(e.g. colons on Unix or semicolons on Windows). Non-existent directories are silently ignored.
In addition to normal directories, individual
PYTHONPATHentries may refer to zipfiles containing pure Python modules (in either source or compiled form). Extension modules cannot be imported from zipfiles.
The default search path is installation dependent, but generally begins with
*prefix*/lib/python*version*(seePYTHONHOMEabove). It is always appended toPYTHONPATH.
An additional directory will be inserted in the search path in front of
PYTHONPATHas described above under Interface options. The search path can be manipulated from within a Python program as the variablesys.path.
- Augment the default search path for module files. The format is the same as the shell’s PATH: one or more directory pathnames separated by os.pathsep (e.g. colons on Unix or semicolons on Windows). Non-existent directories are silently ignored.
- 除了普通目錄之外,各個(gè)PYTHONPATH條目還可以引用包含純Python模塊的zipfile(以源代碼或編譯形式)。擴(kuò)展模塊不能從zipfiles導(dǎo)入。
- 默認(rèn)搜索路徑依賴于安裝,但通常以前綴/lib/pythonversion開始(請(qǐng)參閱上面的PYTHONHOME)。它總是附加到PYTHONPATH。
- 將在PYTHONPATH前面的搜索路徑中插入一個(gè)附加目錄,如上面的接口選項(xiàng)所述。搜索路徑可以在Python程序中作為變量sys.path進(jìn)行操作。
探索
看完上面的官方文檔,如果還是不明白兩者關(guān)系,接著我們繼續(xù)探索下。
創(chuàng)建一個(gè)工程,目錄結(jié)構(gòu)如下:

class Module_A_Class:
def method_A(self):
print('method_A')
if __name__ == '__main__':
pass
from Module_A.Module_A_Class import *
class Module_B_Class:
def method_B(self):
Module_A_Class().method_A()
if __name__ == '__main__':
Module_B_Class().method_B()
在 Module_B 中調(diào)用 Module_A 的函數(shù) method_A,關(guān)系如下:

(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ pwd
/Volumes/HDD/wxx/script/SysPathDemo
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python Module_B/Module_B_Class.py
Traceback (most recent call last):
File "Module_B/Module_B_Class.py", line 14, in <module>
from Module_A.Module_A_Class import *
ModuleNotFoundError: No module named 'Module_A'
終端下執(zhí)行 Module_B_Class.py,報(bào)ModuleNotFoundError 這個(gè)錯(cuò)誤,同樣的代碼,在Pycharm上執(zhí)行,沒有問題。
于是在 Module_B_Class.py 中最上面新增代碼:
import sys
print(sys.path)
from Module_A.Module_A_Class import *
class Module_B_Class:
def method_B(self):
Module_A_Class().method_A()
if __name__ == '__main__':
Module_B_Class().method_B()
終端下執(zhí)行:
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python Module_B/Module_B_Class.py
['/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg']
Traceback (most recent call last):
File "Module_B/Module_B_Class.py", line 14, in <module>
from Module_A.Module_A_Class import *
ModuleNotFoundError: No module named 'Module_A'
Pycharm下執(zhí)行:
/Volumes/HDD/wxx/script/SysPathDemo/venv/bin/python /Volumes/HDD/wxx/script/SysPathDemo/Module_B/Module_B_Class.py
['/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Volumes/HDD/wxx/script/SysPathDemo',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg',
'/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']
method_A
Process finished with exit code 0
可以看出,Pycharm下運(yùn)行,相比于終端,sys.path中多了兩條路徑:
'/Volumes/HDD/wxx/script/SysPathDemo'
'/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend'
回顧上面終端執(zhí)行 Module_B_Class.py 報(bào)的錯(cuò):
ModuleNotFoundError: No module named 'Module_A'
而 Module_A 模塊正好處于 /Volumes/HDD/wxx/script/SysPathDemo路徑之下。
可以看出,Pycharm 自動(dòng)添加模塊路徑到 sys.path 中了。而這個(gè)自動(dòng)步驟,我猜測是通過Module_B_Class.py中的這一句from Module_A.Module_A_Class import *自動(dòng)識(shí)別并添加的。而終端運(yùn)行則不會(huì)自動(dòng)添加。
那么我們通過代碼 或者 終端添加試試:
-
代碼方式添加:
import sys
sys.path.append('/Volumes/HDD/wxx/script/SysPathDemo')
print(sys.path)
from Module_A.Module_A_Class import *
class Module_B_Class:
def method_B(self):
Module_A_Class().method_A()
if __name__ == '__main__':
Module_B_Class().method_B()
終端中執(zhí)行:
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python Module_B/Module_B_Class.py
['/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo']
method_A
這下正常執(zhí)行了。
-
終端通過PYTHONPATH添加:
先移除上面Module_B_Class.py中添加sys.path的這句語句:
sys.path.append('/Volumes/HDD/wxx/script/SysPathDemo')
如下:
import sys
print(sys.path)
from Module_A.Module_A_Class import *
class Module_B_Class:
def method_B(self):
Module_A_Class().method_A()
if __name__ == '__main__':
Module_B_Class().method_B()
終端中執(zhí)行:
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ echo $PYTHONPATH
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ export PYTHONPATH=/Volumes/HDD/wxx/script/SysPathDemo
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ echo $PYTHONPATH
/Volumes/HDD/wxx/script/SysPathDemo
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python Module_B/Module_B_Class.py
['/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Volumes/HDD/wxx/script/SysPathDemo',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo']
method_A
從上面的操作,可以看出,通過添加到 PYTHONPATH 的路徑,會(huì)自動(dòng)添加到 sys.path 中。
還可以知道:
執(zhí)行的
.py文件所在的路徑,會(huì)自動(dòng)添加到sys.path中,如上面的Module_B_Class .py所在路徑/Volumes/HDD/wxx/script/SysPathDemo/Module_B,而且是添加到sys.path中的第一個(gè)。sys.path包含當(dāng)前所依賴的Python環(huán)境。如上面SysPathDemo項(xiàng)目所依賴的Python環(huán)境,是使用virtualenv創(chuàng)建的獨(dú)立Python環(huán)境/Volumes/HDD/wxx/script/SysPathDemo/venv
不清楚 virtualenv的可以參考我的另一篇。
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg'
-
sys.path包含獨(dú)立Python環(huán)境的父環(huán)境,即系統(tǒng)安裝版本號(hào)為3.7.4的Python環(huán)境。
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload'
而通過pip安裝的第三方庫,是包含在~/lib/python3.7/site-packages/..路徑下了,而本地自定義的庫,也就是我們上面的Module_A和Module_B則需要通過代碼的方式,或者終端export PYTHONPATH=/XX/YY/ZZ的方式將模塊所在路徑添加到sys.path,才能在終端中正常執(zhí)行py文件,且PYTHONPATH在重啟終端時(shí)會(huì)被重置為空。
蛋疼啊...
是否有更加便捷的方式呢?
答案是有的。
其他方式
方式一:
將自定義模塊放置到site-packages文件夾中。
這種方式會(huì)導(dǎo)致site-packages的管理混亂。方式二:
使用pth文件,在site-packages文件中創(chuàng)建.pth文件,將模塊的路徑寫進(jìn)去,一行一個(gè)路徑。

.pth內(nèi)容,# 開頭為注釋語句
# SysPathDemo project
/Volumes/HDD/wxx/script/SysPathDemo
Module_B_Class.py內(nèi)容如下
import sys
# sys.path.append('/Volumes/HDD/wxx/script/SysPathDemo')
print(sys.path)
from Module_A.Module_A_Class import *
class Module_B_Class:
def method_B(self):
Module_A_Class().method_A()
if __name__ == '__main__':
Module_B_Class().method_B()
終端執(zhí)行:
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python Module_B/Module_B_Class.py
['/Volumes/HDD/wxx/script/SysPathDemo/Module_B',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg']
method_A
顯然,方式二更為靠譜。
補(bǔ)充
以上的例子,是針對(duì)Module_B獨(dú)立運(yùn)行時(shí),引用了Module_A的代碼。當(dāng)我們執(zhí)行python ../Module_B/Module_B_Class.py時(shí),路徑../Module_B會(huì)被添加到sys.path中。
明白這個(gè)關(guān)系之后,我們在項(xiàng)目中新建一個(gè)MainDemo.py文件,目錄關(guān)系如下:

調(diào)用關(guān)系如下:

代碼如下:
Module_A_Class .py
class Module_A_Class:
def method_A(self):
print('method_A')
if __name__ == '__main__':
pass
Module_B_Class.py
class Module_B_Class:
def method_B(self):
print('method_B')
if __name__ == '__main__':
pass
MainDemoClass.py
import sys
print(sys.path)
from Module_A.Module_A_Class import *
from Module_B.Module_B_Class import *
class MainDemoClass:
def main_method(self):
Module_A_Class().method_A()
Module_B_Class().method_B()
if __name__ == '__main__':
MainDemoClass().main_method()
我們將前面的 .pth內(nèi)容進(jìn)行注釋
# SysPathDemo project
# /Volumes/HDD/wxx/script/SysPathDemo
執(zhí)行python MainDemo.py,如下:
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ pwd
/Volumes/HDD/wxx/script/SysPathDemo
(venv) wuxiaoxindeMac-mini:SysPathDemo wuxiaoxin$ python MainDemo.py
['/Volumes/HDD/wxx/script/SysPathDemo',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python37.zip',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7',
'/Users/wuxiaoxin/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages',
'/Volumes/HDD/wxx/script/SysPathDemo',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Volumes/HDD/wxx/script/SysPathDemo/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg']
method_A
method_B
MainDemo.py中所應(yīng)用的兩個(gè)模塊Module_A和Module_B,所處路徑剛好與MainDemo.py相同。正如我們前面所說的,執(zhí)行Python 命令時(shí),MainDemo.py所在路徑/Volumes/HDD/wxx/script/SysPathDemo,會(huì)被自動(dòng)添加到sys.path中,這也就是我們?yōu)槭裁床恍枰謩?dòng)添加該路徑到sys.path中,也不會(huì)再報(bào)這個(gè)錯(cuò)誤ModuleNotFoundError的原因。
總結(jié)
并列模塊A和B,相互引用的話,需要手動(dòng)添加模塊所在路徑到sys.path中。
這里的模塊所在路徑,假如模塊路徑為/Volumes/HDD/wxx/script/SysPathDemo/Module_A,那么Module_A模塊所在路徑為/Volumes/HDD/wxx/script/SysPathDemo。
方式多種:
(1)代碼方式
import sys
sys.path.append('模塊所在路徑')
(2)終端設(shè)置PYTHONPATH,會(huì)被自動(dòng)添加到sys.path中。缺點(diǎn)是每次重啟終端PYTHONPATH都會(huì)被重置為空。
export PYTHONPATH=模塊所在路徑
(3)將模塊放置到 site-packages 文件夾下,注意你項(xiàng)目依賴的Python 環(huán)境,從而決定放置在系統(tǒng)安裝的Python的site-packages下,還是 virtualenv創(chuàng)建的Python環(huán)境的site-packages下。缺點(diǎn)是不好管理,自定義模塊一多,site-packages下會(huì)很混亂。
(4)在 site-packages 文件夾下,創(chuàng)建 .pth 文件,將模塊路徑添加到該文件中,一行一個(gè)路徑。
這種方式更便于管理,且不需要每次在每個(gè)模塊中新增代碼,也不需要每次打開終端重新設(shè)置。