模塊是什麼 ?python
模塊就是一系列功能(函數)的集合體程序員
模塊的分類bootstrap
一個python文件自己就是一個模塊 , 文件名m.py , 模塊名叫m
dom
ps:模塊分爲4中形式 1.使用python編寫的.py文件 2.已被編譯爲共享庫或DLL的C或C++擴展 3.把一系列模塊組織到一塊兒的文件夾(注:文件夾下有一個__init__.py文件,該文件夾稱之爲包 4.使用C編寫並連接到python解釋器的內置模塊
[建立模塊]函數
# 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名稱空間,不會重複執行代碼
ui
[深刻理解]設計
# 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.能夠在函數中導入模塊
# 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裏面值的內存地址,並非名字 # 只是他們的名字相同又指向相同的內存地址,可是不在一個名稱空間,因此不會相互影響
[導入模塊發生的事]
# 一、產一個模塊的名稱空間 # 二、運行foo.py將運行過程當中產生的名字都丟到模塊的名稱空間去 # 三、在當前名稱空間拿到一個名字,該名字指向模塊名稱空間中的某一個內存地址
[強調]
# 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__控制*表明的名字有哪些
impot導入模塊在使用時必須加前綴"模塊." 優勢:確定不會與當前名稱空間中的名字衝突 缺點:加前綴顯得麻煩 from.. . impat.. .導入模塊在使用時不用加前綴 優勢:代碼更精簡 缺點:容易與當前名稱空間混淆
極力不推薦在程序中設計有模塊的循環導入,可是若是真的遇到,記住兩種解決方式
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: # 將模塊導入的代碼,放在函數體中,這樣保證在運行代碼的時候,函數未調用函數體裏面的代碼不執行
不管是 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導入一個模塊是較耗費內存的 若是你中途從內存中刪除了,再導入就會再開闢內存空間,極大的消耗內存 一樣你在函數內部中導入模塊,函數調用結束後,導入的模塊在內存中也不會回收 只有噹噹前運行文件結束,無其餘文件對模塊中的名字有引用,模塊纔會從內存中消失
咱們在編寫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() # 在被當作腳本執行時,執行此處的代碼
# 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的名稱空間也被回收了 # 總結: 模塊沒有被其餘文件引用就被回收了
若是一個模塊在開發測試的時候,確定會要運行,可是若是被別人導入的時候,不想執行裏面的函數怎麼辦呢?
# 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__ == 模塊名,即去掉後綴的文件名