python中import學(xué)習(xí)備忘筆記
前言
在python的模塊有兩種組織方式,一種是單純的python文件,文件名就是模塊名,一種是包,包是一個(gè)包含了若干python文件的目錄,目錄下必須有一個(gè)文件__init__.py
,這樣目錄名字就是模塊名,包里的python文件也可以通過包名.文件名的方式import
import語法
import語法有兩種
1、直接import模塊
import Module import Module as xx
2、從模塊import對象(下級模塊,類,函數(shù),變量等)
from Module import Name from Module immport Name as yy
as語法是用來設(shè)置對象(這里用對象泛指模塊,類,函數(shù)等等)別名,import將對象名字引入了當(dāng)前文件的名字空間
假設(shè)有如下目錄結(jié)構(gòu)
├── A.py └── pkg ├── B.py └── __init__.py
在當(dāng)前目錄下,以下語句都是有效的
import A import pkg import pkg.B from pkg import B
為了簡化討論,下面將不會對as語法進(jìn)行舉例
import步驟
python所有加載的模塊信息都存放在sys.modules結(jié)構(gòu)中,當(dāng)import一個(gè)模塊時(shí),會按如下步驟來進(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__
中
嵌套import
在import模塊時(shí)我們可能會擔(dān)心一個(gè)模塊會不會被import多次,假設(shè)有A,B,C三個(gè)模塊,A需要import B和C,B又要import C,這樣A會執(zhí)行到兩次import C,一次是自己本身import,一次是在import B時(shí)執(zhí)行的import,但根據(jù)上面講到的import步驟,在第二次import時(shí)發(fā)現(xiàn)模塊已經(jīng)被加載,所以不會重復(fù)import
但如下情況卻會報(bào)錯(cuò)
#filename: A.py from B import BB class AA:pass #filename: B.py from A import AA class BB:pass
這時(shí)不管是執(zhí)行A.py還是B.py都會拋出ImportError的異常,假設(shè)我們執(zhí)行的是A.py,究其原因如下
- 文件A.py執(zhí)行from B import BB,會先掃描B.py,同時(shí)在A的名字空間中為B創(chuàng)建module對象,試圖從B中查找BB
- 掃描B.py第一行執(zhí)行
from A import AA
,此時(shí)又會去掃描A.py - 掃描A.py第一行執(zhí)行
from B import BB
,由于步驟1已經(jīng)為B創(chuàng)建module對象,所以會直接從B的module對象的__dict__
中獲取BB,此時(shí)顯然BB是獲取不到的,于是拋出異常
解決這種情況有兩種辦法,
- 將from B import BB改為import B,或?qū)rom A import AA改為import A
- 將A.py或B.py中的兩行代碼交換位置
總之,import需要注意的是,盡量在需要用到時(shí)再import
包的import
當(dāng)一個(gè)目錄下有__init__.py
文件時(shí),該目錄就是一個(gè)python的包
import包和import單個(gè)文件是一樣的,我們可以這樣類比:
- import單個(gè)文件時(shí),文件里的類,函數(shù),變量都可以作為import的對象
- import包時(shí),包里的子包,文件,以及
__init__.py
里的類,函數(shù),變量都可以作為import的對象
假設(shè)有如下目錄結(jié)構(gòu)
pkg ├── __init__.py └── file.py
其中__init__.py內(nèi)容如下
argument = 0 class A:pass
在和pkg同級目錄下執(zhí)行如下語句都是OK的
>>> import pkg >>> import pkg.file >>> from pkg import file >>> from pkg import A >>> from pkg import argument
但如下語句是錯(cuò)誤的
>>> import pkg.A >>> import pkg.argument
報(bào)錯(cuò)ImportError: No module named xxx
,因?yàn)楫?dāng)我們執(zhí)行import A.B
,A和B都必須是模塊(文件或包)
相對導(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āng)我們有多個(gè)包時(shí),就可能有需求從一個(gè)包import另一個(gè)包的內(nèi)容,這就會產(chǎn)生絕對導(dǎo)入,而這也往往是最容易發(fā)生錯(cuò)誤的時(shí)候,還是以具體例子來說明
目錄結(jié)構(gòu)如下
app ├── __inti__.py ├── mod1 │ ├── file1.py │ └── __init__.py ├── mod2 │ ├── file2.py │ └── __init__.py └── start.py
其中app/start.py
內(nèi)容為import mod1.file1
app/mod1/file1.py
內(nèi)容為from ..mod2 import file2
為了便于分析,我們在所有py文件(包括__init__.py
)第一行加入print __file__, __name__
現(xiàn)在app/mod1/file1.py
里用到了相對導(dǎo)入,我們在app/mod1下執(zhí)行python file1.py
或者在app下執(zhí)行python mod1/file1.py
都會報(bào)錯(cuò)ValueError: Attempted relative import in non-package
在app下執(zhí)行python -m mod1.file1
或python start.py
都會報(bào)錯(cuò)ValueError: Attempted relative import beyond toplevel package
具體原因后面再說,我們先來看一下導(dǎo)入模塊時(shí)的一些規(guī)則
在沒有明確指定包結(jié)構(gòu)的情況下,python是根據(jù)__name__來決定一個(gè)模塊在包中的結(jié)構(gòu)的,如果是__main__則它本身是頂層模塊,沒有包結(jié)構(gòu),如果是A.B.C結(jié)構(gòu),那么頂層模塊是A。
基本上遵循這樣的原則
- 如果是絕對導(dǎo)入,一個(gè)模塊只能導(dǎo)入自身的子模塊或和它的頂層模塊同級別的模塊及其子模塊
- 如果是相對導(dǎo)入,一個(gè)模塊必須有包結(jié)構(gòu)且只能導(dǎo)入它的頂層模塊內(nèi)部的模塊
有目錄結(jié)構(gòu)如下
A ├── B1 │ ├── C1 │ │ └── file.py │ └── C2 └── B2
其中A,B1,B2,C1,C2都為包,這里為了展示簡單沒有列出__init__.py
文件,當(dāng)file.py的包結(jié)構(gòu)為A.B1.C1.file(注意,是根據(jù)__name__
來的,而不是磁盤的目錄結(jié)構(gòu),在不同目錄下執(zhí)行file.py
時(shí)對應(yīng)的包目錄結(jié)構(gòu)都是不一樣的)時(shí),在file.py
中可采用如下的絕對的導(dǎo)入
import A.B1.C2 import A.B2
和如下的相對導(dǎo)入
from .. import C2 from ... import B2
什么情況下會讓file.py的包結(jié)構(gòu)為A.B1.C1.file呢,有如下兩種
- 在A的上層目錄執(zhí)行
python -m A.B1.C1.file
, 此時(shí)明確指定了包結(jié)構(gòu) - 在A的上層目錄建立文件start.py,在
start.py
里有import A.B1.C1.file,然后執(zhí)行python start.py
,此時(shí)包結(jié)構(gòu)是根據(jù)file.py
的__name__
變量來的
再看前面出錯(cuò)的兩種情況,第一種執(zhí)行python file1.py
和python mod1/file1.py
,此時(shí)file.py
的__name__
為__main__
,也就是說它本身就是頂層模塊,并沒有包結(jié)構(gòu),所以會報(bào)錯(cuò)
第二種情況,在執(zhí)行python -m mod1.file1
和python start.py
時(shí),前者明確告訴解釋器mod1是頂層模塊,后者需要導(dǎo)入file1,而file1.py
的__name__
為mod1.file1
,頂層模塊為也mod1,所以在file1.py
中執(zhí)行from ..mod2 import file2
時(shí)會報(bào)錯(cuò) ,因?yàn)閙od2并不在頂層模塊mod1內(nèi)部。通過錯(cuò)誤堆??梢钥闯?,并不是在start.py
中絕對導(dǎo)入時(shí)報(bào)錯(cuò),而是在file1.py
中相對導(dǎo)入報(bào)的錯(cuò)
那么如何才能偶正確執(zhí)行呢,有兩種方法,一種是在app上層目錄執(zhí)行python -m app.mod1.file1
,另一種是改變目錄結(jié)構(gòu),將所有包放在一個(gè)大包中,如下
app ├── pkg │ ├── __init__.py │ ├── mod1 │ │ ├── __init__.py │ │ └── file1.py │ └── mod2 │ ├── __init__.py │ └── file2.py └── start.py
start.py
內(nèi)容改成import pkg.mod1.file1
,然后在app下執(zhí)行python start.py
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者使用python能帶來一定的幫助,如有疑問大家可以留言交流。
相關(guān)文章
在?Python?中創(chuàng)建DataFrame的方法
這篇文章主要介紹了教你如何在?Python?中創(chuàng)建DataFrame,我們將學(xué)習(xí)以多種方式創(chuàng)建DataFrame,DataFrame是數(shù)據(jù)的二維集合,是一種數(shù)據(jù)結(jié)構(gòu),其中數(shù)據(jù)以表格形式存儲,更多相關(guān)資料需要的小伙伴可以參考一下2022-03-03Python人工智能學(xué)習(xí)PyTorch實(shí)現(xiàn)WGAN示例詳解
這篇文章主要為大家介紹了人工智能學(xué)習(xí)PyTorch實(shí)現(xiàn)WGAN的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11Python爬蟲實(shí)戰(zhàn)演練之采集糗事百科段子數(shù)據(jù)
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Python采集糗事百科段子的數(shù)據(jù),大家可以在過程中查缺補(bǔ)漏,提升水平2021-10-10