Python sys.path & PYTHONPATH

參考:

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 of PYTHONPATH.

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 by os.pathsep (e.g. colons on Unix or semicolons on Windows). Non-existent directories are silently ignored.

In addition to normal directories, individual PYTHONPATH entries 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* (see PYTHONHOME above). It is always appended to PYTHONPATH.

An additional directory will be inserted in the search path in front of PYTHONPATH as described above under Interface options. The search path can be manipulated from within a Python program as the variable sys.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)如下:


工程目錄結(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)系如下:

模塊調(diào)用關(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.4Python環(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_AModule_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路徑

.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)系如下:

新增MainDemo.py文件

調(diào)用關(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_AModule_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é)

并列模塊AB,相互引用的話,需要手動(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)安裝的Pythonsite-packages下,還是 virtualenv創(chuàng)建的Python環(huán)境的site-packages下。缺點(diǎn)是不好管理,自定義模塊一多,site-packages下會(huì)很混亂。

(4)在 site-packages 文件夾下,創(chuàng)建 .pth 文件,將模塊路徑添加到該文件中,一行一個(gè)路徑。
這種方式更便于管理,且不需要每次在每個(gè)模塊中新增代碼,也不需要每次打開終端重新設(shè)置。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 模塊和包 一 模塊 1 什么是模塊? 常見的場景:一個(gè)模塊就是一個(gè)包含了python定義和聲明的文件,文件名就是...
    go以恒閱讀 2,348評(píng)論 0 4
  • 當(dāng)前目錄 和 腳本目錄 參考資料:https://techibee.com/python/get-current-...
    ThomasYoungK閱讀 11,885評(píng)論 0 11
  • If you quit from the Python interpreter and enter it agai...
    linyk3閱讀 463評(píng)論 0 0
  • 一般項(xiàng)目中模塊一般會(huì)分布在文件系統(tǒng)的不同路徑中。Python解釋器找到某個(gè)模塊并將其導(dǎo)入命名空間會(huì)遵循一定...
    mysimplebook閱讀 1,747評(píng)論 0 0
  • 一、Python簡介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡介】: Python 是一個(gè)...
    _小老虎_閱讀 6,350評(píng)論 0 10

友情鏈接更多精彩內(nèi)容