python中import的機制
引子:python中 from . import ×××的那個點是表示當前包嗎?
我的理解是 from . import XXX預設的就是在當前程式所在資料夾裡init.py程式中匯入XXX,如果當前程式所在資料夾裡沒有init.py檔案的話,就不能這樣寫,而應該寫成from .A import XXX,A是指當前資料夾下你想匯入的函式(或者其他的)的python程式名,如果你想匯入的函式不在當前資料夾,那麼就有可能用到 from .. import XXX(即上一個資料夾中的init.py),或者from ..A import XXX(即上一個資料夾中的檔案A)
- import一個packet:
import一個資料夾時(python叫packet),就是執行init
如果import這個資料夾下的子檔案下的某個模組時,則先按順序執行父資料夾的所有init.py,再執行該檔案的init.py,再執行該模組。
在每個init.py中最好有all列表。
Python的兩種引入機制
Python 提供了二種引入機制:
- relative import
- absolute import
* relative import相對匯入, 也叫作相對引入,在Python2.5及之前是預設的引入方法。它的使用方法如下:
from .string import a
from ..string import a
from ...string import a
- absolute import絕對匯入,在Python2.6之後以及Python3,完全引用成為Python的預設的引入機制。它的使用方法如下:
from pkg import foo
from pkg.moduleA import foo
要注意的是,需要從包目錄最頂層目錄依次寫下,而不能從中間開始。
由於相對import已經不再使用,後面內容只針對絕對miport。
模組與包的定義
模組:本質就是.py結尾的檔案(邏輯上組織python程式碼)模組的本質就是實現一個功能 檔名就是模組名稱。
包: 一個有init
匯入模組
基本格式:
import 模組名
form 模組名 import *
from 模組名 import 模組名 as 新名稱
匯入模組的本質:
- import 模組名 ===》 將模組中所有的資料賦值給模組名,呼叫時需要模組名.方法名()
- from 模組名 import 方法名 ==》將該方法單獨放到當前檔案執行一遍,呼叫時只需要方法名()即可執行。
匯入模組的詳細機制
1.標準import:
Python中所有載入到記憶體的模組都放在 sys.modules 。當 import 一個模組時首先會在這個列表中查詢是否已經載入了此模組,如果載入了則只是將模組的名字加入到正在呼叫 import 的模組的 Local 名字空間中。如果沒有載入則從 sys.path 目錄中按照模組名稱查詢模組檔案,模組可以是py、pyc、pyd,找到後將模組載入記憶體,並加到 sys.modules 中,並將名稱匯入到當前的 Local 名字空間。
一個模組不會重複載入。多個不同的模組都可以用 import 引入同一個模組到自己的 Local 名字空間,其實背後的 PyModuleObject 物件只有一個。
這裡說一個容易忽略的問題:import 只能匯入模組,不能匯入模組中的物件(類、函式、變數等)。例如:模組 A(A.py)中有個函式 getName,另一個模組不能通過 import A.getName 將 getName函式匯入到本模組,只能用 from A import getName。
2.巢狀import:
1)順序巢狀
例如:本模組匯入 A 模組(import A),A 中又 import B,B 模組又可以 import 其他模組……
這中巢狀比較容易理解,需要注意的一點就是各個模組的 Local 名字空間是獨立的。對於上面的例子,本模組 import A 之後本模組只能訪問模組 A,不能訪問模組 B 及其他模組。雖然模組 B 已經載入到記憶體了,如果訪問還要再明確的在本模組中 import B。
2)迴圈巢狀
例如:
為什麼執行 A 的時候不能載入 D 呢?
如果將 A.py 改為:import B 就可以了。
這是怎麼回事呢?
這跟Python內部 import 的機制是有關的,具體到 from B import D,Python 內部會分成幾個步驟:
(1)在 sys.modules 中查詢符號 “B”
(2)如果符號 B 存在,則獲得符號 B 對應的 module 物件。
然後從 的 dict 中獲得符號 “D” 對應的物件,如果 “D” 不存在,則丟擲異常。
(3)如果符號 B 不存在,則建立一個新的 module 物件 ,注意,此時,module 物件的 dict 為空。
執行 B.py 中的程式碼,填充 的 dict。(在本例中,B.py中又會要求匯入A,所以無法完成對dict的填充)
從 的 dict 中獲得 “D” 對應的物件,如果 “D” 不存在,則丟擲異常。(B.py中的from A import C沒執行完,‘class D:pass’無法執行,D也就無法在dict中找到)
所以這個例子的執行順序如下:
1、執行 A.py 中的 from B import D 。由於是執行的 python A.py,所以在 sys.modules 中並沒有 存在, 首先為 B.py 建立一個 module 物件 () , 注意,這時建立的這個 module 物件是空的,裡邊啥也沒有, 在 Python 內部建立了這個 module 物件之後,就會解析執行 B.py,其目的是填充 這個 dict。
2、執行 B.py中的from A import C 。在執行B.py的過程中,會碰到這一句, 首先檢查sys.modules這個module快取中是否已經存在了, 由於這時快取還沒有快取, 所以類似的,Python內部會為A.py建立一個module物件(), 然後,同樣地,執行A.py中的語句
3、再次執行A.py中的from B import D。 這時,由於在第1步時,建立的物件已經快取在了sys.modules中, 所以直接就得到了, 但是,注意,
— 從整個過程來看,我們知道,這時還是一個空的物件,裡面啥也沒有, 所以從這個module中獲得符號”D”的操作就會丟擲異常。
— 如果這裡只是import B,由於”B”這個符號在sys.modules中已經存在,所以是不會丟擲異常的。
匯入包
匯入包的本質:匯入一個包 就是執行包下的_init_.py檔案
只要一個資料夾下面有個 init.py 檔案,那麼這個資料夾就可以看做是一個包。包匯入的過程和模組的基本一致,只是匯入包的時候會執行此包目錄下的 init.py 而不是模組裡面的語句了。另外,如果只是單純的匯入包,而包的 init.py 中又沒有明確的其他初始化操作,那麼此包下面的模組是不會自動匯入的。
例如:
有下面的包結構:
PA
有如下程式:
import sys import PA.wave #1
import PA.PB1 #2
import PA.PB1.pb1_m as m1 #3
import PA.PB2.pb2_m #4
PA.wave.getName() #5
m1.getName() #6
PA.PB.pb2_m.getName() #7
1) 當執行 #1 後,sys.modules 會同時存在 PA、PA.wave 兩個模組,此時可以呼叫 PA.wave 的任何類或函數了。但不能呼叫 PA.PB1(2) 下的任何模組。當前 Local 中有了 PA 名字。
2) 當執行 #2 後,只是將 PA.PB1 載入記憶體,sys.modules 中會有 PA、 PA.wave、PA.PB1 三個模組,但是 PA.PB1 下的任何模組都沒有自動載入記憶體,此時如果直接執行 PA.PB1.pb1_m.getName() 則會出錯,因為 PA.PB1 中並沒有 pb1_m 。(如果只是單純的匯入包,而包的 init.py 中又沒有明確的其他初始化操作,那麼此包下面的模組是不會自動匯入的。)當前 Local 中還是隻有 PA 名字,並沒有 PA.PB1 名 字。
3) 當執行 #3 後,會將 PA.PB1 下的 pb1_m 載入記憶體,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m 四個模組,此時可以執行 PA.PB1.pb1_m.getName() 了。由於使用了 as,當前 Local中除了 PA 名字,另外添加了 m1 作為 PA.PB1.pb1_m 的別名。
4) 當執行 #4 後,會將 PA.PB2、PA.PB2.pb2_m 載入記憶體,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m、PA.PB2、PA.PB2.pb2_m 六個模組。當前 Local 中還是隻有 PA、m1(筆者:這句話沒有理解?)。
下面的 #5,#6,#7 都是可以正確執行的。
python搜尋路徑
對搜尋路徑的操作
import sys,os
os.path.abspath(__file__) #獲取當前檔案的全名
os.path.dirname() #獲取當前物件的父級目錄
sys.path.insert()#將當前物件的路徑新增到首位
sys.path.append() # 將當前環境變數新增到環境變數的末尾