在python的模塊有兩種組織方式,一種是單純的python文件,文件名就是模塊名,一種是包,包是一個包含了若干python文件的目錄,目錄下必須有一個文件__init__.py,這樣目錄名字就是模塊名,包里的python文件也可以通過包名.文件名的方式import
import語法
import語法有兩種
1、直接import模塊
import Module import Module as xx
2、從模塊import對象(下級模塊,類,函數,變量等)
from Module import Name from Module immport Name as yy
as語法是用來設置對象(這里用對象泛指模塊,類,函數等等)別名,import將對象名字引入了當前文件的名字空間
假設有如下目錄結構
├── A.py └── pkg ├── B.py └── __init__.py
在當前目錄下,以下語句都是有效的
import A import pkg import pkg.B from pkg import B
為了簡化討論,下面將不會對as語法進行舉例
import步驟
python所有加載的模塊信息都存放在sys.modules結構中,當import一個模塊時,會按如下步驟來進行
如果是import A,檢查sys.modules中是否已經有A,如果有則不加載,如果沒有則為A創建module對象,并加載A
如果是from A import B,先為A創建module對象,再解析A,從中尋找B并填充到A的__dict__中
嵌套import
在import模塊時我們可能會擔心一個模塊會不會被import多次,假設有A,B,C三個模塊,A需要import B和C,B又要import C,這樣A會執行到兩次import C,一次是自己本身import,一次是在import B時執行的import,但根據上面講到的import步驟,在第二次import時發現模塊已經被加載,所以不會重復import
但如下情況卻會報錯
#filename: A.py from B import BB class AA:pass #filename: B.py from A import AA class BB:pass
這時不管是執行A.py還是B.py都會拋出ImportError的異常,假設我們執行的是A.py,究其原因如下
文件A.py執行from B import BB,會先掃描B.py,同時在A的名字空間中為B創建module對象,試圖從B中查找BB
掃描B.py第一行執行from A import AA,此時又會去掃描A.py
掃描A.py第一行執行from B import BB,由于步驟1已經為B創建module對象,所以會直接從B的module對象的__dict__中獲取BB,此時顯然BB是獲取不到的,于是拋出異常
解決這種情況有兩種辦法,
將from B import BB改為import B,或將from A import AA改為import A
將A.py或B.py中的兩行代碼交換位置
總之,import需要注意的是,盡量在需要用到時再import
包的import
當一個目錄下有__init__.py文件時,該目錄就是一個python的包
import包和import單個文件是一樣的,我們可以這樣類比:
import單個文件時,文件里的類,函數,變量都可以作為import的對象
import包時,包里的子包,文件,以及__init__.py里的類,函數,變量都可以作為import的對象
假設有如下目錄結構
pkg ├── __init__.py └── file.py
其中__init__.py內容如下
argument = 0 class A:pass
在和pkg同級目錄下執行如下語句都是OK的
>>> import pkg >>> import pkg.file >>> from pkg import file >>> from pkg import A >>> from pkg import argument
但如下語句是錯誤的
>>> import pkg.A >>> import pkg.argument
報錯ImportError: No module named xxx,因為當我們執行import A.B,A和B都必須是模塊(文件或包)
相對導入和絕對導入
絕對導入的格式為import A.B或from A import B,相對導入格式為from . import B或from ..A import B,.代表當前模塊,..代表上層模塊,...代表上上層模塊,依次類推。當我們有多個包時,就可能有需求從一個包import另一個包的內容,這就會產生絕對導入,而這也往往是最容易發生錯誤的時候,還是以具體例子來說明
目錄結構如下
app ├── __inti__.py ├── mod1 │ ├── file1.py │ └── __init__.py ├── mod2 │ ├── file2.py │ └── __init__.py └── start.py
其中app/start.py內容為import mod1.file1
app/mod1/file1.py內容為from ..mod2 import file2
為了便于分析,我們在所有py文件(包括__init__.py)第一行加入print __file__, __name__
現在app/mod1/file1.py里用到了相對導入,我們在app/mod1下執行python file1.py或者在app下執行python mod1/file1.py都會報錯ValueError: Attempted relative import in non-package
在app下執行python -m mod1.file1或python start.py都會報錯ValueError: Attempted relative import beyond toplevel package
具體原因后面再說,我們先來看一下導入模塊時的一些規則
在沒有明確指定包結構的情況下,python是根據__name__來決定一個模塊在包中的結構的,如果是__main__則它本身是頂層模塊,沒有包結構,如果是A.B.C結構,那么頂層模塊是A。
基本上遵循這樣的原則
如果是絕對導入,一個模塊只能導入自身的子模塊或和它的頂層模塊同級別的模塊及其子模塊
如果是相對導入,一個模塊必須有包結構且只能導入它的頂層模塊內部的模塊
有目錄結構如下
A ├── B1 │ ├── C1 │ │ └── file.py │ └── C2 └── B2
其中A,B1,B2,C1,C2都為包,這里為了展示簡單沒有列出__init__.py文件,當file.py的包結構為A.B1.C1.file(注意,是根據__name__來的,而不是磁盤的目錄結構,在不同目錄下執行file.py時對應的包目錄結構都是不一樣的)時,在file.py中可采用如下的絕對的導入
import A.B1.C2 import A.B2
和如下的相對導入
from .. import C2 from ... import B2
什么情況下會讓file.py的包結構為A.B1.C1.file呢,有如下兩種
在A的上層目錄執行python -m A.B1.C1.file, 此時明確指定了包結構
在A的上層目錄建立文件start.py,在start.py里有import A.B1.C1.file,然后執行python start.py,此時包結構是根據file.py的__name__變量來的
再看前面出錯的兩種情況,第一種執行python file1.py和python mod1/file1.py,此時file.py的__name__為__main__ ,也就是說它本身就是頂層模塊,并沒有包結構,所以會報錯
第二種情況,在執行python -m mod1.file1和python start.py時,前者明確告訴解釋器mod1是頂層模塊,后者需要導入file1,而file1.py的__name__為mod1.file1,頂層模塊為也mod1,所以在file1.py中執行from ..mod2 import file2時會報錯 ,因為mod2并不在頂層模塊mod1內部。通過錯誤堆棧可以看出,并不是在start.py中絕對導入時報錯,而是在file1.py中相對導入報的錯
那么如何才能偶正確執行呢,有兩種方法,一種是在app上層目錄執行python -m app.mod1.file1,另一種是改變目錄結構,將所有包放在一個大包中,如下
app ├── pkg │ ├── __init__.py │ ├── mod1 │ │ ├── __init__.py │ │ └── file1.py │ └── mod2 │ ├── __init__.py │ └── file2.py └── start.py
start.py內容改成import pkg.mod1.file1,然后在app下執行python start.py
總結
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com