Learning-Python【16】:模塊的導入使用
一、什麽是模塊
模塊就是一系列功能的集合體,一個模塊就是一個包含了Python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。
模塊有三種來源:
1、內置的模塊
2、第三方的模塊
3、自定義模塊
模塊的四種通用類別:
1、使用Python編寫的代碼(.py文件)
2、已被編譯為共享庫或DLL的C或C++擴展
3、把一系列模塊組織到一起的文件夾(註:文件夾下有一個__init__.py文件,該文件夾稱之為包)
4、使用C編寫並鏈接到Python解釋器的內置模塊
二、為何要用模塊
1、使用內置的或者第三方模塊的好處是:拿來就可以使用,可以極大提升開發效率
2、使用自定義模塊的好處是:可以減少代碼冗余(抽取我們自己程序中要公用的一些功能定義成模塊,然後程序的各部分組件都去模塊中調用共享的功能)
三、模塊的使用
前提:一定要區分開誰是執行文件,誰是被導入模塊
1、import導入模塊
在Pycharm中右鍵新建一個文件,文件名是spam.py,模塊名是spam,再新建一個文件叫run.py,在run.py中
import spam
首次導入一個模塊,會發生以下事情:
1)會產生一個模塊的名稱空間
2)執行文件spam.py,將執行過程中產生的名字都放到模塊的名稱空間中
3)在當前執行文件的名稱空間中拿到一個模塊名,該名字指向模塊的名稱空間
之後的導入,都是直接引用第一次導入的成果,不會重新執行文件
在spam.py文件中添加代碼
money = 1000
def read1():
print(‘spam模塊:‘,money)
def read2():
print(‘spam模塊‘)
read1()
def change():
global money
money = 0
在執行文件中訪問模塊名稱空間中名字的語法:模塊名.名字,即在run.py中
import spam
print(spam.money) # 1000
print(spam.read1) # <function read1 at 0x000001CE16209B70>
print(spam.read2) # <function read2 at 0x000001CE16209A60>
print(spam.change) # <function change at 0x000001CE16209C80>
spam.read1() # spam模塊: 1000
def read1():
print(‘run.py --> read1‘)
read1() # run.py --> read1
spam.read2() # spam模塊
# spam模塊: 1000
money = 9999
spam.change()
print(spam.money) # 0
print(money) # 9999
總結 import 導入模塊:在使用時必須加上模塊名作為前綴
優點:指名道姓的向某一個名稱空間拿取名字,不會與當前名稱空間中的名字沖突
缺點:但凡應用模塊中的名字都需要加前綴,不夠簡潔
還可以一行導入多個模塊,但不推薦使用
import spam, os, time
可以為模塊起別名(註意:模塊名應全為小寫)
import spam as sm
print(sm.money)
print(sm.read1)
2、from...import...導入模塊
首次導入模塊發生的三件事:
1)創建一個模塊的名稱空間
2)執行文件spam.py,將執行過程中產生的名字都放到模塊的名稱空間中
3)在當前執行文件中直接拿到一個名字,該名字就是執行模塊中相對應的名字
刪除上面的兩個文件,重新建一個spam.py,添加代碼
money = 1000
def read1():
print(‘spam模塊:‘,money)
def read2():
print(‘spam模塊‘)
read1()
def change():
global money
money = 0
再新建一個run.py,在run.py中
from spam import money
print(money) # 1000
from spam import money
money = 200
print(money) # 與當前名稱空間中的名字沖突, 結果為200
總結from...import...導入模塊
優點:使用時,無需再加前綴,更簡潔
缺點::容易與當前名稱空間中的名字沖突
現在將run.py裏的代碼改成
from spam import money, read1
money = 200
# 這裏執行read1裏面的money還是1000,因為在定義階段就已經固定死了,與調用位置無關
read1() # spam模塊: 1000
print(money) # 200
還可以導入全部模塊,但不推薦使用
# 星號代表從被導入模塊中拿到所有名字
from spam import *
有一個 __all__ 的功能,將指定的模塊名以字符串的形式存放,如果再使用星號導入模塊,這時導入的就是 __all__裏面的內容,而不是導入全部的模塊,例如:
在spam.py中定義 __all__
__all__ = [‘money‘, ‘read1‘]
money = 1000
def read1():
print(‘spam模塊:‘,money)
def read2():
print(‘spam模塊‘)
read1()
def change():
global money
money = 0
在run.py中用星號導入
from spam import *
read1() # spam模塊: 1000
read2() # 報錯, 因為 __all__沒有包含read2模塊
可以為模塊起別名
from spam import read1 as r1
r1()
四、Python文件的兩種執行方式
1、直接運行
2、作為模塊導入
# 在m1.py中
def f1():
print(‘f1‘)
print(__name__) # 執行後是 __main__
# 在run.py中
import m1 # 執行後是 m1
即:當做腳本運行,__name__ 等於‘__main__‘,當做模塊導入,__name__等於模塊名
所以這個條件是:if __name__ == ‘__main__‘: ,用來控制.py文件在不同的應用場景下執行不同的邏輯
# 在m1.py中
def f1():
print(‘f1‘)
if __name__ == ‘__main__‘:
f1()
# 在run.py中
import m1
五、模塊的搜索路徑
1、模塊搜索路徑的優先級
1)內存中已經加載過的
2)內置模塊
3)sys.path(第一個值是當前執行文件所在的文件夾)
模塊搜索路徑優先從內存中已加載過的開始查找,例如我新建一個m1.py和一個run.py,在run.py中導入模塊m1
# m1.py
def f1():
print(‘from f1‘)
# run.py
import time
import m1
time.sleep(10) # 程序睡眠10秒的過程中刪除m1.py
import m1
m1.f1() # 這裏還可以執行
刪除m1.py文件後再導入執行不會報錯,因為在刪除的過程中程序沒有結束,再次導入是優先從內存中查找,找到了直接運行,但如果再次運行就會報錯,因為內存已經回收,找不到m1模塊了
註意:不應該將自己的模塊名命名成與內置模塊或第三方模塊的名字相同
2、添加sys.path
新建m1.py和run.py,將m1.py放在dir1文件夾下,讓dir1文件夾和run.py在同一級目錄
# m1.py def f1(): print(‘from f1‘)
f1() # run.py import m1 # 這時候是找不到的,但是如果添加sys.path,將run.py改成如下
# run.py import sys sys.path.append(‘E://Test//dir1‘) # 將m1所在的文件夾路徑添加到sys.path import m1
3、from...import...
還是上面的情況,可以在run.py裏面通過from...import...導入
from dir1 import m1
這是在sys.path中找到的,但是我沒有添加sys.path,為什麽也能找到呢?
因為sys.path的第一個值是當前執行文件所在的文件夾,也就是Test文件夾(我的run.py和dir1的上一層是Test文件夾),所以說是以當前執行文件的sys.path為準,可以找到dir1,進而找到m1
基於上面的目錄,我在dir1下新建一個文件夾叫dir2,在dir2中新建一個文件叫m2.py,現在的路徑關系是:Test下面有一個run.py和dir1,dir1下有一個m1.py和dir2,dir2下有一個m2.py
m2中添加代碼
def f2():
print(‘from f2‘)
f2()
可以通過from...import...導入
from dir1.dir2 import m2
第一種特殊情況:模塊的絕對導入
刪除上面的所有文件,新建run.py和dir1文件夾,在dir1下新建m1.py和m2.py
# m1.py
def f1():
print(‘from f1‘)
# m2.py
def f2():
print(‘from f2‘)
run想訪問m1,因為不在同一級目錄,所以要通過上面講到的兩種方法(添加sys.path或from...import導入)來訪問(我用後者)
# run.py
from dir1 import m1
現在我執行run.py,可以通過run訪問到m1
現在m1想訪問m2,只需要在m1中導入m2即可
# m1.py
import m2
def f1():
print(‘from f1‘)
m2.f2()
但是現在,我再執行run.py,能否通過run訪問到m1再訪問到m2呢?
不行!因為sys.path是以當前執行文件為準的,那麽sys.path的第一個值就是 E://Test//,在m1裏面導入m2的時候,去sys.path中無法找到m2,所以會報錯
強調:所有被導入的模塊參照的環境變量sys.path都是以執行文件為準的
那麽怎麽解決呢
# m1.py
from dir1 import m2
def f1():
print(‘from f1‘)
m2.f2()
# m2.py
def f2():
print(‘from f2‘)
# run.py
from dir1 import m1
m1.f1()
註意:不要理解成sys.path是以執行文件所在的文件夾為準,因為sys.path有很多個值,而執行文件所在的文件夾僅僅只是sys.path中的一個值,這個執行文件所在的文件夾找不到後面還有很多值可以找
上面這種情況是以執行文件的sys.path作為參考點開始導入,稱之為模塊的絕對導入
優點:在執行文件與被導入的模塊中都可以使用
缺點:所有導入都是以sys.path為參考點,導入麻煩
第二種特殊情況:模塊的相對導入
參照當前所在文件的文件夾為起始開始查找,稱之為模塊的相對導入
符號:.(一個點)代表當前所在文件的文件夾,..(兩個點)代表上一級文件夾,...(三個點)代表上一級的上一級文件夾
優點:導入更加簡單
缺點:只能在被導入的模塊中使用,不能在執行文件中用
現在在Test下新建一個dir0,dir0下新建一個dir1和run.py,dir1下新建m1.py和m2.py
# run.py
from dir0.dir1 import m1 # 用的還是絕對導入
m1.f1()
# m1.py
from . import m2 # 用的是相對導入
def f1():
print(‘from f1‘)
m2.f2()
# m2.py
def f2():
print(‘from f2‘)
Learning-Python【16】:模塊的導入使用