淺析Python中的絕對(duì)導(dǎo)入與相對(duì)導(dǎo)入
如果您從事的Python項(xiàng)目有多個(gè)文件,那么您以前可能不得不使用import語(yǔ)句。即使對(duì)于擁有多個(gè)項(xiàng)目的Python重度使用者(比如我),import也可能會(huì)造成混淆!您可能正在閱讀本文,因?yàn)槟雽?duì)Python中的import(尤其是絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入)有更深入的了解。
1 Imports快速介紹
Python模塊是具有.py擴(kuò)展名的文件,而Python包是其中具有模塊的任何文件夾(或者在Python 2中是包含__init__.py文件的文件夾)。當(dāng)一個(gè)模塊中的代碼需要訪問(wèn)另一模塊或程序包中的代碼時(shí),你需要導(dǎo)入它。
但是如何一個(gè)模塊,假設(shè)您像這樣導(dǎo)入os模塊:
import os
Python要做的第一件事是在 sys.modules中查找名為os系統(tǒng)模塊. 這是以前導(dǎo)入的所有模塊的緩存。sys模塊提供了一系列關(guān)于Python運(yùn)行環(huán)境的變量和函數(shù)。如果在模塊緩存中找不到該名稱,Python將繼續(xù)搜索內(nèi)置模塊的列表。這些模塊與Python一起預(yù)裝,可以在Python標(biāo)準(zhǔn)庫(kù)中找到。如果在內(nèi)置模塊中仍然找不到該名稱,Python就會(huì)在sys.path定義的目錄列表中搜索它。該列表通常包括當(dāng)前目錄,首先搜索該目錄。
總結(jié)來(lái)說(shuō),Python尋找一個(gè)模塊主要有以下三個(gè)步驟:
1 通過(guò)sys.modules從已經(jīng)加載的模塊中尋找
2 從Python標(biāo)準(zhǔn)庫(kù)中尋找,Python標(biāo)注庫(kù)就是那些通過(guò)pip install安裝來(lái)的模塊
3 通過(guò)sys.path包含的目錄列表尋找,sys.path通常會(huì)自動(dòng)導(dǎo)入當(dāng)前目錄,當(dāng)然sys.path也可以添加自己指定的路徑
當(dāng)Python找到該模塊時(shí),它將其綁定到本地范圍內(nèi)的一個(gè)名稱。這意味著現(xiàn)在已經(jīng)定義了os,并且可以在當(dāng)前文件中使用os,而不會(huì)拋出ModuleNotFoundError。如果沒(méi)找到模塊就拋出ModuleNotFoundError,如下所示:
import os
但是要注意的另外一個(gè)問(wèn)題是,導(dǎo)入模塊,會(huì)出現(xiàn)安全問(wèn)題。請(qǐng)注意,Python的導(dǎo)入系統(tǒng)存在一些重大的安全風(fēng)險(xiǎn)。這主要是由于其靈活性。例如,模塊緩存是可寫的,并且可以使用導(dǎo)入系統(tǒng)覆蓋Python的核心功能。從第三方程序包導(dǎo)入還會(huì)使您的應(yīng)用程序面臨安全威脅。
2 import語(yǔ)句的語(yǔ)法
現(xiàn)在您知道了導(dǎo)入語(yǔ)句的工作原理,讓我們探究它們的語(yǔ)法。您可以導(dǎo)入軟件包和模塊。(請(qǐng)注意,導(dǎo)入軟件包實(shí)際上是將軟件包的__init__.py文件作為模塊導(dǎo)入。)您還可以從軟件包或模塊中導(dǎo)入特定的對(duì)象。
通常有兩種類型的導(dǎo)入語(yǔ)法。直接使用模塊時(shí),可以直接導(dǎo)入模塊,如下所示:
2.1 基本使用
import os
os可以是包或模塊。當(dāng)您使用第二種語(yǔ)法時(shí),您將從另一個(gè)包或模塊中導(dǎo)入。下面是是一個(gè)實(shí)例
from os import path
path可以是模塊,子包或?qū)ο?,例如類或函?shù)。您還可以選擇重命名導(dǎo)入的資源,如下所示:
import os as so
這將把導(dǎo)入的os重命名為so。現(xiàn)在必須將其引用為so,否則將無(wú)法識(shí)別它。
2.2 導(dǎo)入聲明的樣式
PEP 8 是Python的官方樣式指南,在編寫導(dǎo)入語(yǔ)句時(shí)有一些提示。PEP 8詳細(xì)見(jiàn)https://pep8.org/#imports。
總結(jié)如下:
- 導(dǎo)入應(yīng)始終寫在文件頂部,在任何模塊注釋和文檔字符串之后。
- import應(yīng)該根據(jù)用途分為以下三類:
- 標(biāo)準(zhǔn)庫(kù)導(dǎo)入(Python的內(nèi)置模塊)
- 相關(guān)的第三方導(dǎo)入(已安裝但不屬于當(dāng)前應(yīng)用程序的模塊)
- 本地應(yīng)用程序?qū)耄▽儆诋?dāng)前應(yīng)用程序的模塊)
- 每個(gè)import都要用空格分隔
在每個(gè)導(dǎo)入組中按字母順序排列導(dǎo)入也是一個(gè)好主意。這使得查找特定導(dǎo)入變得更加容易,特別是當(dāng)一個(gè)文件中有許多導(dǎo)入時(shí)。以下是如何設(shè)置導(dǎo)入語(yǔ)句樣式的示例。以下的import語(yǔ)句分為三個(gè)不同的組,用空格隔開(kāi)。在每個(gè)組中,它們也按字母順序排列。
'''
格式化的import如下所示
'''
# 標(biāo)準(zhǔn)庫(kù)
import datetime
import os
# 第三方庫(kù)
from flask import Flask
# 本地庫(kù)
# import local_module
3 絕對(duì)import和相對(duì)import
3.1 絕對(duì)import
您已經(jīng)掌握了如何編寫import語(yǔ)句以及如何像專業(yè)人士那樣設(shè)計(jì)它們的樣式?,F(xiàn)在是時(shí)候?qū)W習(xí)一點(diǎn)關(guān)于絕對(duì)導(dǎo)入的知識(shí)了。絕對(duì)導(dǎo)入指定要導(dǎo)入的資源使用其從項(xiàng)目根文件夾中的完整路徑。
假設(shè)您具有以下目錄結(jié)構(gòu):
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
當(dāng)前目錄project其中包含兩個(gè)子目錄package1和package2。該package1目錄有兩個(gè)文件,module1.py和module2.py。
該package2目錄包含三個(gè)文件:兩個(gè)模塊module3.py和module4.py,以及一個(gè)初始化文件__init__.py。它還包含一個(gè)目錄,subpackage該目錄又包含一個(gè)文件module5.py。
讓我們假設(shè)以下內(nèi)容:
- package1/module2.py包含一個(gè)函數(shù)function1。
- package2/__init__.py包含一個(gè)類class1。
- package2/subpackage1/module5.py包含一個(gè)函數(shù)function2。
以下是絕對(duì)導(dǎo)入的實(shí)際示例:
from package1 import module1 from package1.module2 import function1 from package2 import class1 from package2.subpackage1.module5 import function2
module2中的內(nèi)容如下:
def function1():
passspackage2中__init__.py中的內(nèi)容如下:
class class1():
def __init__():
returnpackage2.subpackage1.module5中的內(nèi)容如下:
def function2():
pass請(qǐng)注意,必須為每個(gè)包或文件提供來(lái)自頂級(jí)包文件夾的詳細(xì)路徑。這有點(diǎn)類似于它的文件路徑,但是我們使用點(diǎn)(.)而不是斜杠(/)。
對(duì)導(dǎo)入是首選,因?yàn)樗鼈兎浅G宄椭苯?。僅通過(guò)查看語(yǔ)句,就可以很容易地準(zhǔn)確知道導(dǎo)入的資源在哪里。此外,即使import語(yǔ)句的當(dāng)前位置發(fā)生更改,絕對(duì)導(dǎo)入仍然有效。實(shí)際上,PEP 8明確建議絕對(duì)導(dǎo)入。
但是,有時(shí)絕對(duì)導(dǎo)入可能會(huì)變得非常冗長(zhǎng),具體取決于目錄結(jié)構(gòu)的復(fù)雜性。想象一下這樣的聲明:
from package1.subpackage2.subpackage3.subpackage4.module5 import function6
太荒謬了吧?幸運(yùn)的是,在這種情況下,相對(duì)導(dǎo)入是一個(gè)不錯(cuò)的選擇!
3.2 相對(duì)導(dǎo)入
相對(duì)導(dǎo)入指定相對(duì)于當(dāng)前位置(即import語(yǔ)句所在的位置)要導(dǎo)入的資源。有兩種類型的相對(duì)導(dǎo)入:隱式和顯式。隱式相對(duì)導(dǎo)入在Python3中已被棄用,因此我將不在這里介紹它們。
相對(duì)導(dǎo)入的語(yǔ)法取決于當(dāng)前位置以及要導(dǎo)入的模塊,包或?qū)ο蟮奈恢?。以下是相?duì)導(dǎo)入的一些示例:
from .some_module import some_class from ..some_package import some_function from . import some_class
您可以看到在上面的每個(gè)import語(yǔ)句中至少有一個(gè)點(diǎn)。相對(duì)導(dǎo)入使用點(diǎn)表示法來(lái)指定位置。
單點(diǎn)表示所引用的模塊或軟件包與當(dāng)前位置位于同一目錄中。兩個(gè)點(diǎn)表示它位于當(dāng)前位置的父目錄中,即上面的目錄中。三個(gè)點(diǎn)表示它位于祖父母目錄中,依此類推。如果您使用類似Unix的操作系統(tǒng),這可能對(duì)您來(lái)說(shuō)很熟悉!
假設(shè)您具有與以前相同的目錄結(jié)構(gòu):
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
您可以function1通過(guò)package1/module1.py以下方式導(dǎo)入文件:
# package1/module1.py from .module2 import function1
你可以將class1和function2導(dǎo)入到package2/module3.py文件中:
# package2/module3.py from . import class1 from .subpackage1.module5 import function2
在第一個(gè)import語(yǔ)句中,單點(diǎn)表示您正在class1從當(dāng)前包中導(dǎo)入。請(qǐng)記住,導(dǎo)入軟件包實(shí)際上會(huì)將軟件包的__init__.py文件導(dǎo)入為模塊。
在第二個(gè)import語(yǔ)句中,您將再次使用一個(gè)點(diǎn),因?yàn)閟ubpackage1它與當(dāng)前模塊位于同一目錄中module3.py。
當(dāng)然這種方法有個(gè)極大問(wèn)題,可能會(huì)報(bào)錯(cuò),如下所示:
ModuleNotFoundError: No module named '__main__.module2'; '__main__' is not a package
這是相對(duì)導(dǎo)入只有在父模塊已經(jīng)在當(dāng)前運(yùn)行環(huán)境中被導(dǎo)入過(guò)才有用,所以盡可能用絕對(duì)導(dǎo)入。解決辦法見(jiàn):
相對(duì)導(dǎo)入的利與弊
相對(duì)導(dǎo)入的一個(gè)明顯優(yōu)勢(shì)是它們非常簡(jiǎn)潔。根據(jù)當(dāng)前位置,他們可以將您之前看到的可笑的冗長(zhǎng)的import語(yǔ)句變成如下所示的簡(jiǎn)單內(nèi)容:
from …subpackage4.module5 import function6
不幸的是,相對(duì)導(dǎo)入可能會(huì)很混亂,尤其是對(duì)于目錄結(jié)構(gòu)可能會(huì)更改的共享項(xiàng)目。相對(duì)導(dǎo)入也不如絕對(duì)導(dǎo)入更易讀,而且很難說(shuō)出導(dǎo)入資源的位置。
以上就是淺析Python中的絕對(duì)導(dǎo)入與相對(duì)導(dǎo)入的詳細(xì)內(nèi)容,更多關(guān)于Python絕對(duì)導(dǎo)入與相對(duì)導(dǎo)入的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python pygame實(shí)現(xiàn)擋板彈球游戲
這篇文章主要為大家詳細(xì)介紹了python pygame實(shí)現(xiàn)擋板彈球游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Python prettytable模塊應(yīng)用詳解
PrettyTable 是python中的一個(gè)第三方庫(kù),可用來(lái)生成美觀的ASCII格式的表格,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-09
Python實(shí)現(xiàn)希爾排序算法的原理與用法實(shí)例分析
這篇文章主要介紹了Python實(shí)現(xiàn)希爾排序算法,簡(jiǎn)單講述了希爾排序的原理并結(jié)合具體實(shí)例形式分析了Python希爾排序的具體實(shí)現(xiàn)方法與使用技巧,需要的朋友可以參考下2017-11-11
python利用插值法對(duì)折線進(jìn)行平滑曲線處理
這篇文章主要為大家詳細(xì)介紹了python利用插值法對(duì)折線進(jìn)行平滑曲線處理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
MacOS(M1芯片?arm架構(gòu))下安裝tensorflow的詳細(xì)過(guò)程
這篇文章主要介紹了MacOS(M1芯片?arm架構(gòu))下如何安裝tensorflow,本節(jié)使用的版本是tensorflow2.4?python3.8,因此并未安裝加速插件,本文結(jié)合實(shí)例代碼詳細(xì)講解,需要的朋友可以參考下2023-02-02
詳解Python的collections模塊中的deque雙端隊(duì)列結(jié)構(gòu)
deque結(jié)構(gòu)可以看作是內(nèi)置的list結(jié)構(gòu)的加強(qiáng)版,且比隊(duì)列提供了更強(qiáng)大的方法,下面就通過(guò)幾個(gè)小例子來(lái)詳解Python的collections模塊中的deque雙端隊(duì)列結(jié)構(gòu):2016-07-07
熵值法原理及Python實(shí)現(xiàn)的示例詳解
熵值法也稱熵權(quán)法,是學(xué)術(shù)研究及實(shí)際應(yīng)用中的一種常用且有效的編制指標(biāo)的方法。本文就來(lái)和大家聊聊熵值法原理及Python實(shí)現(xiàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-02-02

