詳解Python中的相對導(dǎo)入和絕對導(dǎo)入
前言
Python 相對導(dǎo)入與絕對導(dǎo)入,這兩個概念是相對于包內(nèi)導(dǎo)入而言的。包內(nèi)導(dǎo)入即是包內(nèi)的模塊導(dǎo)入包內(nèi)部的模塊。
Python import 的搜索路徑
- 在當(dāng)前目錄下搜索該模塊
- 在環(huán)境變量 PYTHONPATH 中指定的路徑列表中依次搜索
- 在 Python 安裝路徑的 lib 庫中搜索
Python import 的步驟
python 所有加載的模塊信息都存放在 sys.modules 結(jié)構(gòu)中,當(dāng) import 一個模塊時,會按如下步驟來進(jìn)行
- 如果是
import A,檢查sys.modules中是否已經(jīng)有 A,如果有則不加載,如果沒有則為 A 創(chuàng)建 module 對象,并加載 A - 如果是
from A import B,先為 A 創(chuàng)建 module 對象,再解析A,從中尋找B并填充到 A 的__dict__中
相對導(dǎo)入與絕對導(dǎo)入
絕對導(dǎo)入的格式為 import A.B 或 from A import B,相對導(dǎo)入格式為 from . import B 或 from ..A import B,.代表當(dāng)前模塊,..代表上層模塊,...代表上上層模塊,依次類推。
相對導(dǎo)入可以避免硬編碼帶來的維護(hù)問題,例如我們改了某一頂層包的名,那么其子包所有的導(dǎo)入就都不能用了。但是 存在相對導(dǎo)入語句的模塊,不能直接運(yùn)行,否則會有異常:
ValueError: Attempted relative import in non-package
這是什么原因呢?我們需要先來了解下導(dǎo)入模塊時的一些規(guī)則:
在沒有明確指定包結(jié)構(gòu)的情況下,Python 是根據(jù) __name__ 來決定一個模塊在包中的結(jié)構(gòu)的,如果是 __main__ 則它本身是頂層模塊,沒有包結(jié)構(gòu),如果是A.B.C 結(jié)構(gòu),那么頂層模塊是 A?;旧献裱@樣的原則:
- 如果是絕對導(dǎo)入,一個模塊只能導(dǎo)入自身的子模塊或和它的頂層模塊同級別的模塊及其子模塊
- 如果是相對導(dǎo)入,一個模塊必須有包結(jié)構(gòu)且只能導(dǎo)入它的頂層模塊內(nèi)部的模塊
如果一個模塊被直接運(yùn)行,則它自己為頂層模塊,不存在層次結(jié)構(gòu),所以找不到其他的相對路徑。
Python2.x 缺省為相對路徑導(dǎo)入,Python3.x 缺省為絕對路徑導(dǎo)入。絕對導(dǎo)入可以避免導(dǎo)入子包覆蓋掉標(biāo)準(zhǔn)庫模塊(由于名字相同,發(fā)生沖突)。如果在 Python2.x 中要默認(rèn)使用絕對導(dǎo)入,可以在文件開頭加入如下語句:
from __future__ import absolute_import
from __future__ import absolute_import
這句 import 并不是指將所有的導(dǎo)入視為絕對導(dǎo)入,而是指禁用 implicit relative import(隱式相對導(dǎo)入), 但并不會禁掉 explicit relative import(顯示相對導(dǎo)入)。
那么到底什么是隱式相對導(dǎo)入,什么又是顯示的相對導(dǎo)入呢?我們來看一個例子,假設(shè)有如下包結(jié)構(gòu):
thing ├── books │ ├── adventure.py │ ├── history.py │ ├── horror.py │ ├── __init__.py │ └── lovestory.py ├── furniture │ ├── armchair.py │ ├── bench.py │ ├── __init__.py │ ├── screen.py │ └── stool.py └── __init__.py
那么如果在 stool 中引用 bench,則有如下幾種方式:
import bench # 此為 implicit relative import from . import bench # 此為 explicit relative import from furniture import bench # 此為 absolute import
隱式相對就是沒有告訴解釋器相對于誰,但默認(rèn)相對與當(dāng)前模塊;而顯示相對則明確告訴解釋器相對于誰來導(dǎo)入。以上導(dǎo)入方式的第三種,才是官方推薦的,第一種是官方強(qiáng)烈不推薦的,Python3 中已經(jīng)被廢棄,這種方式只能用于導(dǎo)入 path 中的模塊。
相對與絕對僅針對包內(nèi)導(dǎo)入而言
最后再次強(qiáng)調(diào),相對導(dǎo)入與絕對導(dǎo)入僅針對于包內(nèi)導(dǎo)入而言,要不然本文所討論的內(nèi)容就沒有意義。所謂的包,就是包含 __init__.py 文件的目錄,該文件在包導(dǎo)入時會被首先執(zhí)行,該文件可以為空,也可以在其中加入任意合法的 Python 代碼。
相對導(dǎo)入可以避免硬編碼,對于包的維護(hù)是友好的。絕對導(dǎo)入可以避免與標(biāo)準(zhǔn)庫命名的沖突,實際上也不推薦自定義模塊與標(biāo)準(zhǔn)庫命令相同。
前面提到含有相對導(dǎo)入的模塊不能被直接運(yùn)行,實際上含有絕對導(dǎo)入的模塊也不能被直接運(yùn)行,會出現(xiàn) ImportError:
ImportError: No module named XXX
這與絕對導(dǎo)入時是一樣的原因。要運(yùn)行包中包含絕對導(dǎo)入和相對導(dǎo)入的模塊,可以用 python -m A.B.C 告訴解釋器模塊的層次結(jié)構(gòu)。
有人可能會問:假如有兩個模塊 a.py 和 b.py 放在同一個目錄下,為什么能在 b.py 中 import a 呢?
這是因為這兩個文件所在的目錄不是一個包,那么每一個 python 文件都是一個獨立的、可以直接被其他模塊導(dǎo)入的模塊,就像你導(dǎo)入標(biāo)準(zhǔn)庫一樣,它們不存在相對導(dǎo)入和絕對導(dǎo)入的問題。相對導(dǎo)入與絕對導(dǎo)入僅用于包內(nèi)部。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者使用python能有一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
Python實現(xiàn)獲取內(nèi)網(wǎng)IP地址的方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了五種利用Python語言實現(xiàn)獲取內(nèi)網(wǎng)IP地址的方法,文中的示例代碼講解詳細(xì),具有一定的參考價值,需要的可以了解一下2023-03-03
在VSCode中添加Python解釋器并安裝Python庫的方法
這篇文章主要介紹了在VSCode中添加Python解釋器并安裝Python庫的方法,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
基于Python函數(shù)的作用域規(guī)則和閉包(詳解)
下面小編就為大家分享一篇基于Python函數(shù)的作用域規(guī)則和閉包詳解,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11
利用Python實現(xiàn)一個類似MybatisPlus的簡易SQL注解
在實際開發(fā)中,根據(jù)業(yè)務(wù)拼接SQL所需要考慮的內(nèi)容太多了,于是,有沒有一種辦法,可以像MyBatisPlus一樣通過配置注解實現(xiàn)SQL注入呢?本文給大家介紹了如何利用Python實現(xiàn)一個類似MybatisPlus的簡易SQL注解,需要的朋友可以參考下2025-08-08
基于django channel實現(xiàn)websocket的聊天室的方法示例
這篇文章主要介紹了基于基于django channel實現(xiàn)websocket的聊天室的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04

