Python包模塊與模塊導(dǎo)入查找順序詳解
簡(jiǎn)介
無(wú)論是為了看懂別人的代碼,還是為了更好的組織我們的個(gè)人的工程代碼,了解一下Python的模塊和包都非常有必要。
另外,知道搜索模塊的順序,也能幫助我們更好的理解一些常見的錯(cuò)誤,方便我們快速定位問(wèn)題。
模塊導(dǎo)入查找順序
首先,我們先看一下Python的模塊查找順序:
- sys.modules查找(導(dǎo)入模塊緩存)
- 執(zhí)行腳本所在的目錄:__name__==‘__main__’
- 環(huán)境變量PYTHONPATH指定的目錄
- 標(biāo)準(zhǔn)庫(kù)目錄(Python安裝跟目錄下的Lib目錄)
- 配置文件(Python安裝跟目錄下.pth文件中指定的目錄)
- 三方庫(kù)(Python安裝跟目錄下site-packages中的目錄)
搜索路徑是由sys.path決定,我們可以打印看一下:
import sys print(sys.path)
輸出:
['E:\\app\\python\\fin\\md', 'E:\\app\\python\\fin', 'E:\\app\\python\\fin\\.venv\\Scripts\\python313.zip', 'D:\\Env\\python\\py3130i\\DLLs', 'D:\\Env\\python\\py3130i\\Lib', 'D:\\Env\\python\\py3130i', 'E:\\app\\python\\fin\\.venv', 'E:\\app\\python\\fin\\.venv\\Lib\\site-packages']
我們可以手動(dòng)添加搜索目錄和順序:
import sys
# 添加在末尾,最后查找
sys.path.append('dir')
# 添加在開頭,最先查找
sys.path.insert(0, 'dir2')
# 支持zip
sys.path.append('module.zip')
# 支持zip中指定目錄
sys.path.append('module.zip/lib/python')
入口文件所在目錄

環(huán)境變量PYTHONPATH
- Linux系統(tǒng):
echo 'export PYTHONPATH="/home/tim/pylib/"' >> ~/.bashrc source ~/.bashrc

- Windows:

Lib目錄(標(biāo)準(zhǔn)庫(kù))

.pth配置文件
注意._pth和.pth的區(qū)別
._pth:安裝包中沒(méi)有這個(gè)文件要自己創(chuàng)建,內(nèi)嵌包中有

內(nèi)容如下:
python311.zip . D:\\Env\\python\\py3114\\Lib\\site-packages\\ # Uncomment to run site.main() automatically #import site
._pth會(huì)覆蓋sys.path
而.pth是添加sys.path
.pth的處理邏輯在標(biāo)準(zhǔn)庫(kù)site中:

新版本的.pth文件有很多限制,可以參考:site
注意:
這個(gè)文件名字必須要對(duì),不知道,可以直接復(fù)制安裝目錄上的PythonXXX.dll這個(gè)文件的名字。
另外,一般不需要修改,因?yàn)檫@個(gè)文件會(huì)覆蓋sys.path,讓其他的都失效。
三方庫(kù)
標(biāo)準(zhǔn)庫(kù)Lib下的site-packages目錄

模塊
Python中的模塊就是指py文件,一個(gè)文件代表一個(gè)模塊。
包
很多時(shí)候,一個(gè)工程不止一個(gè)模塊,我們可以用包來(lái)組織不同模塊。
包中包含一個(gè)__init__.py文件,用來(lái)控制包的導(dǎo)入行為。
在2.x中__init__.py文件是必修有,3.x可選。
import
import 模塊名
Python解釋器第1次導(dǎo)入模塊就加載到內(nèi)存,多次import不會(huì)重新執(zhí)行模塊內(nèi)的語(yǔ)句
我們看一個(gè)示例:
我們有一個(gè)base_module.py文件
print('這是一個(gè)自定義基本模塊')
num = 1000
def fun_a():
print('fun_a', num)
def fun_b():
print('fun_b')
if __name__=='__main__':
print('執(zhí)行了該文件')
我們?cè)趍odule_import_test.py文件中導(dǎo)入base_module模塊(文件)
import sys print(sys.modules) import base_module # 多了base_module模塊信息 print(sys.modules) base_module.fun_a() import base_module print(sys.modules) base_module.fun_a()

注意:文件名字不要用中劃線,否則import模塊會(huì)出錯(cuò)。
我們可以看到?jīng)]有打印:執(zhí)行了該文件,這是因?yàn)槲覀儗?dǎo)入的是模塊,沒(méi)有直接執(zhí)行這個(gè)文件。
現(xiàn)在是不是就更清楚為啥要加上:if name==‘main’:了
if __name__=='__main__':
print('執(zhí)行了該文件')
from mmm import fffvvv
從包或者模塊中導(dǎo)入函數(shù)、變量等。
和直接import模塊不同,from方式相當(dāng)于將模塊指定的元素導(dǎo)入到當(dāng)前命名空間。
因此,我們可以直接調(diào)用方法,不用通過(guò)模塊名.xxx的方式調(diào)用了。
from base_module import fun_a,fun_b fun_a() fun_b()

from mmm import fff as new_name
還可以用as重命名:
from base_module import fun_a as xxx xxx()
form mmm import *
*表示將模塊中除了下劃線(_)開頭的元素全部元素導(dǎo)入
from base_module import * fun_a() fun_b() print(num)
__all__控制import *的行為
我們?cè)谀K中添加__all__,注意是一個(gè)字符串列表,下面是fun_a
print('這是一個(gè)自定義基本模塊')
num = 1000
def fun_a():
print('fun_a', num)
def fun_b():
print('fun_b')
__all__ = ['fun_a']
if __name__=='__main__':
print('執(zhí)行了該文件')
我們?cè)僬{(diào)用fun_b(),就會(huì)出現(xiàn)NameError:

相對(duì)導(dǎo)入與絕對(duì)導(dǎo)入
- pkg_b.py
num_b = "num_b"
def fun_b():
print("pkg_b fun_b")
- pkg_c.py
from .pkg_b import fun_b
from . import pkg_b
num_c = "num_c"
def fun_c():
print("pkg_c fun_c")
fun_b()
print(f"pkg_c中導(dǎo)入pkg_b:{pkg_b.num_b}")
- base_import.py
import pkg_test.sub_pkg.pkg_b from pkg_test.sub_pkg import pkg_b from pkg_test.sub_pkg import pkg_c pkg_test.sub_pkg.pkg_b.fun_b() pkg_b.fun_b() pkg_c.fun_c() pkg_c.pkg_b.fun_b()

相對(duì)導(dǎo)入的模塊不能作為主程序運(yùn)行,因?yàn)?會(huì)被替換為__main__,然后就會(huì)出現(xiàn):
ImportError: attempted relative import with no known parent package

小結(jié)
導(dǎo)入包和導(dǎo)入模塊基本一致,如何判斷是包還是模塊呢?
包通常是有結(jié)構(gòu),所以,看到import xxx.yyy這種包含.的import,就是導(dǎo)入的包。
import 包名.模塊名 as 別名 from . import 模塊名 from 包名 import 模塊名 as 別名 from 包名.模塊名 import 成員名 as 別名
更多內(nèi)容可以參考:
命名空間
Python的命名空間有點(diǎn)類似于變量表。
- local namespace,就是函數(shù)的命名空間,相當(dāng)于局部變量表,包含函數(shù)的變量
- global namespace,就是模塊的命名空間,包含functions、classes、modules、變量、常量等
- build-in namespace,包含build-in function、exceptions,所有模塊均可訪問(wèn)
Python對(duì)于變量的查找順序?yàn)椋?/p>
- local namespace,當(dāng)前函數(shù)或類方法,若找到,則停止搜索
- global namespace,當(dāng)前模塊,若找到,則停止搜索
- build-in namespace,最后查找build-in命名空間
- 若變量不是build-in的內(nèi)置函數(shù)或變量,Python將報(bào)錯(cuò)NameError
閉包比較特殊:在local namespace找不到該變量,會(huì)查找目標(biāo)是父函數(shù)的local namespace
看一個(gè)簡(jiǎn)單的示例:
print('這是一個(gè)自定義基本模塊')
num = 1000
def fun_a():
print('fun_a', num)
def fun_b():
print('fun_b')
if __name__=='__main__':
fun_a()
print('執(zhí)行了該文件')
from base_module import fun_a,num print(num) num = 5000 print(num) fun_a()

我們可以看到,改變了num值,調(diào)用模塊中的方法的時(shí)候,還是方法的模塊直接的命名空間的num。
各類文件與目錄
- .pyo:優(yōu)化編譯后的文件
- .pyc:Python編譯之后的文件
- .pyd:包含Python代碼的庫(kù)文件(Windows)
- .pyi:靜態(tài)類型信息文件
- .so:Linux的動(dòng)態(tài)鏈接庫(kù)
- .dll:Windows的動(dòng)態(tài)鏈接庫(kù)
- __pycache__:用于存儲(chǔ)編譯Python源文件的字節(jié)碼文件目錄(sys.dont_write_bytecode開關(guān)控制)
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python數(shù)據(jù)分析與處理(二)——處理中國(guó)地區(qū)信息
這篇文章主要介紹了Python數(shù)據(jù)分析與處理-處理中國(guó)地區(qū)信息,上文介紹了北京高考分?jǐn)?shù)線統(tǒng)計(jì)分析,這篇文章依然圍繞Python數(shù)據(jù)分析與處理的相關(guān)資料來(lái)介紹處理中國(guó)地區(qū)信息,需要的朋友可以參考一下2021-12-12
pandas重置索引標(biāo)簽的實(shí)現(xiàn)示例
在使用Pandas進(jìn)行數(shù)據(jù)處理時(shí),有時(shí)候我們可能會(huì)需要對(duì)數(shù)據(jù)進(jìn)行重置索引的操作,本文主要介紹了pandas重置索引標(biāo)簽的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04
Python reduce函數(shù)作用及實(shí)例解析
這篇文章主要介紹了Python reduce函數(shù)作用及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
Python破解excel進(jìn)入密碼的過(guò)程詳解
這篇文章主要為大家介紹了如何利用Python破解excel進(jìn)入密碼,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Python有一定幫助,感興趣的可以動(dòng)手試一試2022-03-03
Python實(shí)現(xiàn)雙向RNN與堆疊的雙向RNN的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Python語(yǔ)言實(shí)現(xiàn)雙向RNN與堆疊的雙向RNN,文中詳細(xì)講解了雙向RNN與堆疊的雙向RNN的原理及實(shí)現(xiàn),需要的可以參考一下2022-07-07
Pytorch微調(diào)BERT實(shí)現(xiàn)命名實(shí)體識(shí)別
命名實(shí)體識(shí)別(NER)是自然語(yǔ)言處理(NLP)中的一項(xiàng)關(guān)鍵任務(wù),它涉及識(shí)別和分類文本中的關(guān)鍵實(shí)體,BERT是一種強(qiáng)大的語(yǔ)言表示模型,在各種 NLP 任務(wù)中顯著提高了性能,包括 NER,在本文中,我們將展示如何使用 PyTorch 對(duì)預(yù)訓(xùn)練的 BERT 模型進(jìn)行微調(diào),以用于 NER 任務(wù)2025-03-03
python中Requests發(fā)送json格式的post請(qǐng)求方法
這篇文章主要介紹了python中Requests發(fā)送json格式的post請(qǐng)求方法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2022-09-09
Python實(shí)現(xiàn)求取表格文件某個(gè)區(qū)域內(nèi)單元格的最大值
這篇文章主要介紹基于Python語(yǔ)言,基于Excel表格文件內(nèi)某一列的數(shù)據(jù),計(jì)算這一列數(shù)據(jù)在每一個(gè)指定數(shù)量的行的范圍內(nèi)(例如每一個(gè)4行的范圍內(nèi))的區(qū)間最大值的方法,需要的朋友可以參考下2023-08-08

