Python包模塊與模塊導入查找順序詳解
簡介
無論是為了看懂別人的代碼,還是為了更好的組織我們的個人的工程代碼,了解一下Python的模塊和包都非常有必要。
另外,知道搜索模塊的順序,也能幫助我們更好的理解一些常見的錯誤,方便我們快速定位問題。
模塊導入查找順序
首先,我們先看一下Python的模塊查找順序:
- sys.modules查找(導入模塊緩存)
- 執(zhí)行腳本所在的目錄:__name__==‘__main__’
- 環(huán)境變量PYTHONPATH指定的目錄
- 標準庫目錄(Python安裝跟目錄下的Lib目錄)
- 配置文件(Python安裝跟目錄下.pth文件中指定的目錄)
- 三方庫(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']
我們可以手動添加搜索目錄和順序:
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目錄(標準庫)

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

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

新版本的.pth文件有很多限制,可以參考:site
注意:
這個文件名字必須要對,不知道,可以直接復制安裝目錄上的PythonXXX.dll這個文件的名字。
另外,一般不需要修改,因為這個文件會覆蓋sys.path,讓其他的都失效。
三方庫
標準庫Lib下的site-packages目錄

模塊
Python中的模塊就是指py文件,一個文件代表一個模塊。
包
很多時候,一個工程不止一個模塊,我們可以用包來組織不同模塊。
包中包含一個__init__.py文件,用來控制包的導入行為。
在2.x中__init__.py文件是必修有,3.x可選。
import
import 模塊名
Python解釋器第1次導入模塊就加載到內(nèi)存,多次import不會重新執(zhí)行模塊內(nèi)的語句
我們看一個示例:
我們有一個base_module.py文件
print('這是一個自定義基本模塊')
num = 1000
def fun_a():
print('fun_a', num)
def fun_b():
print('fun_b')
if __name__=='__main__':
print('執(zhí)行了該文件')
我們在module_import_test.py文件中導入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模塊會出錯。
我們可以看到?jīng)]有打?。簣?zhí)行了該文件,這是因為我們導入的是模塊,沒有直接執(zhí)行這個文件。
現(xiàn)在是不是就更清楚為啥要加上:if name==‘main’:了
if __name__=='__main__':
print('執(zhí)行了該文件')
from mmm import fffvvv
從包或者模塊中導入函數(shù)、變量等。
和直接import模塊不同,from方式相當于將模塊指定的元素導入到當前命名空間。
因此,我們可以直接調(diào)用方法,不用通過模塊名.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 *
*表示將模塊中除了下劃線(_)開頭的元素全部元素導入
from base_module import * fun_a() fun_b() print(num)
__all__控制import *的行為
我們在模塊中添加__all__,注意是一個字符串列表,下面是fun_a
print('這是一個自定義基本模塊')
num = 1000
def fun_a():
print('fun_a', num)
def fun_b():
print('fun_b')
__all__ = ['fun_a']
if __name__=='__main__':
print('執(zhí)行了該文件')
我們再調(diào)用fun_b(),就會出現(xiàn)NameError:

相對導入與絕對導入
- 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中導入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()

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

小結(jié)
導入包和導入模塊基本一致,如何判斷是包還是模塊呢?
包通常是有結(jié)構(gòu),所以,看到import xxx.yyy這種包含.的import,就是導入的包。
import 包名.模塊名 as 別名 from . import 模塊名 from 包名 import 模塊名 as 別名 from 包名.模塊名 import 成員名 as 別名
更多內(nèi)容可以參考:
命名空間
Python的命名空間有點類似于變量表。
- local namespace,就是函數(shù)的命名空間,相當于局部變量表,包含函數(shù)的變量
- global namespace,就是模塊的命名空間,包含functions、classes、modules、變量、常量等
- build-in namespace,包含build-in function、exceptions,所有模塊均可訪問
Python對于變量的查找順序為:
- local namespace,當前函數(shù)或類方法,若找到,則停止搜索
- global namespace,當前模塊,若找到,則停止搜索
- build-in namespace,最后查找build-in命名空間
- 若變量不是build-in的內(nèi)置函數(shù)或變量,Python將報錯NameError
閉包比較特殊:在local namespace找不到該變量,會查找目標是父函數(shù)的local namespace
看一個簡單的示例:
print('這是一個自定義基本模塊')
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)用模塊中的方法的時候,還是方法的模塊直接的命名空間的num。
各類文件與目錄
- .pyo:優(yōu)化編譯后的文件
- .pyc:Python編譯之后的文件
- .pyd:包含Python代碼的庫文件(Windows)
- .pyi:靜態(tài)類型信息文件
- .so:Linux的動態(tài)鏈接庫
- .dll:Windows的動態(tài)鏈接庫
- __pycache__:用于存儲編譯Python源文件的字節(jié)碼文件目錄(sys.dont_write_bytecode開關(guān)控制)
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python數(shù)據(jù)分析與處理(二)——處理中國地區(qū)信息
這篇文章主要介紹了Python數(shù)據(jù)分析與處理-處理中國地區(qū)信息,上文介紹了北京高考分數(shù)線統(tǒng)計分析,這篇文章依然圍繞Python數(shù)據(jù)分析與處理的相關(guān)資料來介紹處理中國地區(qū)信息,需要的朋友可以參考一下2021-12-12
Python實現(xiàn)雙向RNN與堆疊的雙向RNN的示例代碼
這篇文章主要為大家詳細介紹了如何利用Python語言實現(xiàn)雙向RNN與堆疊的雙向RNN,文中詳細講解了雙向RNN與堆疊的雙向RNN的原理及實現(xiàn),需要的可以參考一下2022-07-07
Pytorch微調(diào)BERT實現(xiàn)命名實體識別
命名實體識別(NER)是自然語言處理(NLP)中的一項關(guān)鍵任務,它涉及識別和分類文本中的關(guān)鍵實體,BERT是一種強大的語言表示模型,在各種 NLP 任務中顯著提高了性能,包括 NER,在本文中,我們將展示如何使用 PyTorch 對預訓練的 BERT 模型進行微調(diào),以用于 NER 任務2025-03-03
python中Requests發(fā)送json格式的post請求方法
這篇文章主要介紹了python中Requests發(fā)送json格式的post請求方法,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,感興趣的朋友可以參考一下2022-09-09
Python實現(xiàn)求取表格文件某個區(qū)域內(nèi)單元格的最大值
這篇文章主要介紹基于Python語言,基于Excel表格文件內(nèi)某一列的數(shù)據(jù),計算這一列數(shù)據(jù)在每一個指定數(shù)量的行的范圍內(nèi)(例如每一個4行的范圍內(nèi))的區(qū)間最大值的方法,需要的朋友可以參考下2023-08-08

