用 Jest 進行單元測試
阿新 • • 發佈:2020-12-12
1.模組介紹
1.1 模組的概念
模組是什麼 ?
模組就是一系列功能(函式)的集合體
模組的分類
- 內建模組 c語言寫的模組
- 第三方模組 別人寫好的模組
- 自定義模組 自己自定義的
一個python檔案本身就是一個模組 , 檔名m.py , 模組名叫m
ps:模組分為4中形式 1.使用python編寫的.py檔案 2.已被編譯為共享庫或DLL的C或C++擴充套件 3.把一系列模組組織到一起的資料夾(注:資料夾下有一個__init__.py檔案,該資料夾稱之為包 4.使用C編寫並連結到python直譯器的內建模組
1.2 為什麼要用模組
- 內建與第三方的模組拿來就用,無需定義,這種拿來主義,可以極大的提升自己的開發效率
- 可以將程式的各部分功能提取出來放到一模組中為大家共享使用
- 減少了程式碼冗餘,程式組織結構更加清晰
2. 模組的兩種匯入方式
2.1 import
[建立模組]
# m.py
print('======m=====')
x = 100
def get():
print("get ========%s" % x)
def change():
global x
x += 1
return x
[匯入模組]
# demo.py import m # 匯入m模組 x = 1 y = 2 # ======m===== # 列印l ======m===== ,說明匯入模組,會執行模組裡面的程式碼
[思考] 首次模組會發生什麼事情
1.產生m.py的名稱空間
2.執行m.py程式碼,將m.py執行過程中產生的名字都丟到m的名稱空間
3.在當前檔案中產生一個名字m,指向1中產生的名稱空間
[之後匯入]
之後的匯入,都是直接引用第一次匯入產生的m.py名稱空間,不會重複執行程式碼
[深入理解]
# demo.py import m print(m.x) # 100 print(m.get) # <function get at 0x000001FD80567620> print(m.change) # <function change at 0x000001FD81616510>
[強調]
- 模組名.名字,是指名道姓的問某一個模組要名字對應的值,不會與當前名稱空間的名字發生衝突
- 無論是檢視還是修改操作的都是以定義階段為準(原模組本身),與呼叫位置無關
# demo.py
import m
x = 333
m.get() # get ======== 100
m.change() # 改變的是m名稱空間裡面的x值
print(x) # 333
print(m.x) # 101
[補充]
# 1.可以以逗號為分隔符在一行匯入多個模組
# 建議如下所示匯入多個模組,層次清晰
import time
import random
import m
# 不建議在一行同時匯入多個模組
import time, random, m
# 2.匯入模組的規範
# 1.python內建模組
# 2.第三方模組
# 3.程式設計師自定義模組
import time
import sys
import 第三方模組1
import 第三方模組2
import 自定義模組1
import 自定義模組1
# 3.import ... as ...
# 為匯入的模組起一個別名,以後可以通過別名.名字,取到模組裡面的名字
# 適用於比較長的第三方模組
import jhcsdfveubvf as f
# 4.模組是第一類物件,和變數名,函式名一樣,可以被賦值,可以當引數,返回值,容器的元素
# 5.自定義模組的命名應該採用純小寫+下劃線風格,py2中還是駝峰體
# 6.可以在函式中匯入模組
2.2 from ... import ....
# foo.py
print('======foo=====')
x = 0
def get():
print("get ========%s" % x)
def change():
global x
x += 1
return x
# run.py
from foo import X # x=模組foo中值0的記憶體地址
from foo import get
from foo import change
print(x) # 0
print(get) # <function get at 0x0000029FBD8E7620>
print(change) # <function change at 0x0000029FBE996510>
# 注意:x,get,change這三個名字是在run的名稱空間中,但是指向的都是foo裡面值的記憶體地址,並不是名字
# 只是他們的名字相同又指向相同的記憶體地址,但是不在一個名稱空間,所以不會相互影響
[匯入模組發生的事]
# 1、產一個模組的名稱空間
# 2、執行foo.py將執行過程中產生的名字都丟到模組的名稱空間去
# 3、在當前名稱空間拿到一個名字,該名字指向模組名稱空間中的某一個記憶體地址
[強調]
# run.py
from foo import X # x=模組foo中值0的記憶體地址
from foo import get
from foo import change
print(x) # 0
print(get) # <function get at 0x0000029FBD8E7620>
print(change) # <function change at 0x0000029FBE996510>
x = 3333 # 改變全域性名稱空間中x的指向,現在x指向值3333的記憶體地址
print(x) # 3333
get() # 仍然列印x=0,因為get中的x是引用foo名稱空間中x指向的值,foo中x的指向並沒有變
change() # 改變foo中x的記憶體地址的指向
get() # 列印1
print(x) # 雖然foo中x的指向傳送改變了,但是run中的這個x並不會發生變化,還是原來的記憶體地址0
from foo import x # 這個時候x就指向,foo裡面x指向的新地址了
print(x) # 列印 1
[補充]
#一行匯入多個名字(不推薦)
from foo import X, get,change
# *:匯入模組中的所有名字,當用到一個模組中超級多的名字時,可以用*,但是最好也要規避掉重名的問題
from foo import *|
print(x)
# 起別名
from foo import get as g # 針對get起了一個別名g
[瞭解]
# 瞭解 : __all__ = ["名字1","名字2",.....],列表的元素全是字串格式的名字
# 匯入*,實際上就是把列表中的名字全匯入進入了,可以通過__all__控制*代表的名字有哪些
2.3 兩種方式的比較
impot匯入模組在使用時必須加字首"模組."
優點:肯定不會與當前名稱空間中的名字衝突
缺點:加字首顯得麻煩
from.. . impat.. .匯入模組在使用時不用加字首
優點:程式碼更精簡
缺點:容易與當前名稱空間混淆
3. 迴圈匯入
極力不推薦在程式中設計有模組的迴圈匯入,但是如果真的遇到,記住兩種解決方式
m1.py
print("正在匯入m1")
from m2 import y
x = 1
m2.py
print("正在匯入m2")
from m1 import x
y = 2
run.py
import m1
# 執行程式碼,首先會載入m1的名稱空間,然後執行裡面的程式碼,當執行到第二句的時候,此時m1的名稱空間中還沒有x這個名字,由於是首次匯入會載入m2的名稱空間,執行m2的程式碼,所以控制檯會列印正在匯入m1,正在匯入m2,當運行當m2的第二句程式碼時,由於m1不是首次匯入,所以不會再載入名稱空間,也不會執行程式碼,但是m2問m1要x這個名字,由於m1中的x這個名字還沒有載入到名稱空間,所以程式報錯
[解決方法]
# 方式1:
# 將m1,和m2中的from m2 import y,from m1 import x,放在檔案的最後一行,確保匯入的時候,名字已經完全載入到名稱空間了
# 方式2:
# 將模組匯入的程式碼,放在函式體中,這樣保證在執行程式碼的時候,函式未呼叫函式體裡面的程式碼不執行
4. 搜尋模組路徑的優先順序
無論是 import還是from ...import 在匯入模組時都涉及到查詢問題
[優先順序]
- 記憶體 (內建模組,預設會自動載入到記憶體中)
- 硬碟 : 按照
sys.path(環境變數)
中存放的檔案的順序依次查詢要匯入的模組
import sys
print(sys.path)
['E:\\project\\python\\s29code\\day21', 'E:\\project\\python\\s29code', 'E:\\developTools\\pycharm\\PyCharm 2020.1\\plugins\\python\\helpers\\pycharm_display', 'E:\\developTools\\Anaconda3\\python36.zip', 'E:\\developTools\\Anaconda3\\DLLs', 'E:\\developTools\\Anaconda3\\lib', 'E:\\developTools\\Anaconda3', 'E:\\developTools\\Anaconda3\\lib\\site-packages', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\win32', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\win32\\lib', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\Pythonwin', 'E:\\developTools\\pycharm\\PyCharm 2020.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
# 其中第一個資料夾是當前執行檔案所在的資料夾 E:\\project\\python\\s29code\\day21
# 其中第二個資料夾是當前專案的資料夾 E:\\project\\python\\s29code
# 這個值是pycharm自動新增的,所以當他不存在,值裡面的壓縮包就當資料夾看待
[補充]
通過sys.modules
檢視已將載入到記憶體的模組,內容格式 {'模組名':'記憶體地址'}
print('foo' in sys.modules) # 判斷foo模組是否在當前記憶體中
print('m1' in sys.modules) # 判斷foo模組是否在當前記憶體中
{'builtins': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_thread': <module '_thread' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>,...}
[python的優化機制]
匯入模組會發生模組名和對應的記憶體地址的捆綁關係,但是你通過del 模組名
是無法從記憶體中把那塊空間回收的,因為python匯入一個模組是較耗費記憶體的
如果你中途從記憶體中刪除了,再匯入就會再開闢記憶體空間,極大的消耗記憶體
同樣你在函式內部中匯入模組,函式呼叫結束後,匯入的模組在記憶體中也不會回收
只有噹噹前執行檔案結束,無其他檔案對模組中的名字有引用,模組才會從記憶體中消失
5. 模組規範寫法
我們在編寫py檔案時,需要時刻提醒自己,該檔案既是給自己用的,也有可能會被其他人使用,
因而程式碼的可讀性與易維護性顯得十分重要,為此我們在編寫一個模組時最好按照統一的規範去編寫,如下
"The module is used to..." # 模組的文件描述
import sys # 匯入模組
x = 1 # 定義全域性變數,如果非必須,則最好使用區域性變數,這樣可以提高程式碼的易維護性,並且可以節省記憶體提高效能
class Foo: # 定義類,並寫好類的註釋
'Class Foo is used to...'
pass
def test(): # 定義函式,並寫好函式的註釋
'Function test is used to…'
pass
if __name__ == '__main__': # 主程式
test() # 在被當做指令碼執行時,執行此處的程式碼
6. py檔案的用途
6.1 當做模組匯入
# foo.py
print('======m=====')
x = 0
def get():
print("get ========%s" % x)
def change():
global x
x += 1
return x
# run.py
import foo # 首次匯入模組,執行程式碼
#print(foo)
print(foo.x) # 0
foo.change() # 把x指向的記憶體地址改變了,指向了1
print(foo.x) # 1
x = 1
y = 2
print('11111')
print('22222')
# 名稱空間的回收順序
首次匯入foo模組,foo程式碼會執行,但是程式碼執行結束後它的名稱空間沒有被回收,因為裡面的名字被run.py引用了,緊接著run.py中的程式碼執行完畢後,它的名稱空間裡面的名字沒有被其他引用,執行完後,run.py的名稱空間就被回收了,foo的名稱空間也被回收了
# 總結: 模組沒有被其他檔案引用就被回收了
5.2 當做程式執行
如果一個模組在開發測試的時候,肯定會要執行,但是如果被別人匯入的時候,不想執行裡面的函式怎麼辦呢?
# foo.py
print('======m=====')
x = 100
def get():
print("get ========%s" % x)
def change():
global x
x += 1
return x
if __name__ == '__main__':
# 當作程式執行的時候做的事情
get()
change()
else:
# 被當做模組匯入的時候做的事情
pass
# 每一個py檔案內部都有一個__name__變數,
# 當檔案當做程式執行的時候,__name__ == __main__
# 當檔案當做模組匯入時,__name__ == 模組名,即去掉字尾的檔名