1 什么是模塊
模塊就是一系列功能的集合體,分為三大類
I:內(nèi)置的模塊(Python解釋器提供)
II:第三方的模塊(其他人編寫)
III:自定義的模塊(開發(fā)人員自己編寫)
一個(gè)python文件本身就是一個(gè)模塊,文件名m.py,模塊名叫m
ps:模塊有四種形式
1 使用python編寫的.py文件
2 已被編譯為共享庫(kù)或DLL的C或C++擴(kuò)展
3 把一系列模塊組織到一起的文件夾(注:文件夾下有一個(gè)__init__.py文件,該文件夾稱之為包)
4 使用C編寫并鏈接到python解釋器的內(nèi)置模塊
2 為何要用模塊
I:內(nèi)置與第三方的模塊拿來就用,無需定義,這種拿來主義,可以極大地提升自己的開發(fā)效率
II:自定義的模塊
可以將程序的各部分功能提取出來放到模塊中為大家共享使用
好處是減少了代碼冗余,程序組織結(jié)構(gòu)更加清晰
3 如何用模塊(import導(dǎo)入模塊)
- foo.py模塊文件
print("模塊foo==>")
x=1
def get():
print(x)
def change():
global x
x=0
- 模塊.py, 代碼文件, 調(diào)用foo模塊
y = 333
z = 444
import foo # foo這個(gè)名字屬于模塊.py這個(gè)文件的名稱空間,指向的是foo.py模塊文件的名稱空間
# 執(zhí)行模塊.py文件后,會(huì)導(dǎo)入foo模塊,然后會(huì)執(zhí)行foo.py文件中的代碼,因此,會(huì)在控制臺(tái)輸出以下內(nèi)容
模塊foo==>
1. 首次導(dǎo)入模塊會(huì)發(fā)生3件事
1、產(chǎn)生foo.py文件的名稱空間,將foo.py運(yùn)行過程中產(chǎn)生的名字都丟到foo.py的名稱空間中,本示例中,foo.py文件有x變量名,get和change函數(shù)名
2、執(zhí)行foo.py內(nèi)部代碼
3、在當(dāng)前文件(也就是調(diào)用foo模塊的文件,這里就是模塊.py文件)中產(chǎn)生的有一個(gè)名字foo,該名字指向1中產(chǎn)生的名稱空間, 也就是foo.py文件的名稱空間,另外還會(huì)產(chǎn)生本身該模塊.py文件包含的名字,y和z

image.png
之后的導(dǎo)入,都是直接引用首次導(dǎo)入產(chǎn)生的foo.py名稱空間,不會(huì)重復(fù)執(zhí)行代碼
import foo
import foo
import foo
import foo
2. 如何引用模塊中的功能
# 模塊.py
print(foo.x)
print(foo.get)
print(foo.change)
強(qiáng)調(diào)1:模塊名.名字,是指名道姓地問某一個(gè)模塊要名字對(duì)應(yīng)的值,不會(huì)與當(dāng)前名稱空間中的名字發(fā)生沖突
x=1111111111111
print(x) # 1111111111111
print(foo.x) # 1
1
<function get at 0x0000018C348AB4C0>
# print(模塊.函數(shù)名字)返回的是該名字在模塊的名稱空間的內(nèi)存地址, print(模塊.函數(shù)名字())返回的是該函數(shù)的返回值
<function change at 0x0000018C34B66670>
強(qiáng)調(diào)2:
無論是查看還是修改,操作的都是模塊本身,與調(diào)用位置無關(guān)
模塊的名稱空間查找關(guān)系以模塊定義階段的查找關(guān)系為準(zhǔn)
# 模塊.py
import foo
x=3333333333
foo.get() # 1
foo.change() # 沒有結(jié)果,因?yàn)閏hange函數(shù)沒有print
print(x) # 3333333333
print(foo.x) # 0,該代碼執(zhí)行在了foo.change()后,因此,此時(shí)change函數(shù)內(nèi)的global x,已經(jīng)把x改為了0,所以結(jié)果為0
foo.get() # 0,再次執(zhí)行結(jié)果為0,因?yàn)榇藭r(shí)global的x已經(jīng)被change函數(shù)修改為0了
3、可以以逗號(hào)為分隔符在一行導(dǎo)入多個(gè)模塊
建議如下所示導(dǎo)入多個(gè)模塊
import time
import foo
import m
不建議在一行同時(shí)導(dǎo)入多個(gè)模塊
import time,foo,m
4、導(dǎo)入模塊的規(guī)范
I. python內(nèi)置模塊
II. 第三方模塊
III. 程序員自定義模塊
import time
import sys
import 第三方1
import 第三方2
import 自定義模塊1
import 自定義模塊2
import 自定義模塊3
5. import ... as ...
import foo as f # f=foo,別名,相當(dāng)于把foo在調(diào)用文件的名稱空間的內(nèi)存地址給了f,通過f找到foo,再找到foo.py
f.get()
6、模塊是第一類對(duì)象
import foo
可以賦值, 可以當(dāng)做函數(shù)的參數(shù), 函數(shù)的返回值等, 和函數(shù)一樣都是第一類對(duì)象
7、自定義模塊的命名應(yīng)該采用純小寫+下劃線的風(fēng)格
8、可以在函數(shù)內(nèi)導(dǎo)入模塊
def func():
import foo
9、模塊py文件存放位置
內(nèi)置模塊:由python解釋器提供,py文件形式的內(nèi)置模塊會(huì)存到python解釋器安裝的路徑下
對(duì)于Linux或者M(jìn)acOS,路徑案例:/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
對(duì)于Windows,路徑案例:C:\Users\David\AppData\Local\Programs\Python\Python36\Lib
第三方模塊:一般通過pip下載
對(duì)于Linux或者M(jìn)acOS,路徑案例:/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages
對(duì)于Windows,路徑案例:C:\Users\David\AppData\Local\Programs\Python\Python36\Lib\site-packages
對(duì)于虛擬環(huán)境,無論是哪個(gè)平臺(tái),以py文件形式存在的內(nèi)置模塊都存放在解釋器安裝的路徑,不會(huì)專門復(fù)制一份,存到虛擬環(huán)境的目錄中
而對(duì)于第三方模塊,會(huì)單獨(dú)存放在虛擬環(huán)境目錄lib/site-packages,因?yàn)樘摂M環(huán)境目的就是區(qū)分開不同項(xiàng)目的不同第三方模塊
4. __name__變量
當(dāng)一個(gè)python文件, 被當(dāng)做模塊導(dǎo)入時(shí), __name__的值為模塊名
當(dāng)一個(gè)Python文件, 被運(yùn)行時(shí), 其值為'__main__'
用途: 當(dāng)模塊文件直接被當(dāng)做程序運(yùn)行時(shí), 會(huì)運(yùn)行其定義的代碼, 而如果被導(dǎo)入其他文件時(shí), 又不運(yùn)行代碼
用法: 判斷__name__的變量值是否等于'__main__
foo.py
# coding:utf8
x = 10
def get():
print('foo.py的get方法')
def change():
print('foo.py的change方法')
global x
x = 20
if __name__ == '__main__':
print('文件被執(zhí)行')
get()
change()
else:
print('文件被導(dǎo)入')
pass
運(yùn)行結(jié)果:
文件被執(zhí)行 # 因?yàn)槟K文件自身被運(yùn)行時(shí), 會(huì)運(yùn)行if判斷內(nèi)的代碼
foo.py的get方法
foo.py的change方法
code.py
import foo
運(yùn)行結(jié)果:
文件被導(dǎo)入 # 被當(dāng)做模塊導(dǎo)入時(shí), 不會(huì)運(yùn)行if判斷內(nèi)的代碼, 因?yàn)楸粚?dǎo)入時(shí),__name__的變量值就是模塊名本身
5. from ... import 導(dǎo)入模塊
impot導(dǎo)入模塊在使用時(shí)必須加前綴"模塊."
優(yōu)點(diǎn):肯定不會(huì)與當(dāng)前名稱空間中的名字沖突
缺點(diǎn):加前綴顯得麻煩
from ... import ...導(dǎo)入也發(fā)生了三件事
產(chǎn)一個(gè)模塊的名稱空間
運(yùn)行foo.py將運(yùn)行過程中產(chǎn)生的名字都丟到模塊的名稱空間去
在當(dāng)前名稱空間拿到一個(gè)名字,該名字與模塊名稱空間中的同名的變量指向同一個(gè)內(nèi)存地址
注意: 在模塊的名稱空間做的變量賦值引發(fā)的內(nèi)存地址的改變, 是不會(huì)影響引用了該模塊的py文件的自身的映射關(guān)系, 其內(nèi)部還是只想定義階段的內(nèi)存地址, 除非重新導(dǎo)入模塊, 刷新名稱空間關(guān)系
from...impot...導(dǎo)入模塊在使用時(shí)不用加前綴
優(yōu)點(diǎn):代碼更精簡(jiǎn)
缺點(diǎn):容易與當(dāng)前名稱空間混淆
foo.py
x = 1
from foo import x # x=模塊foo中值1的內(nèi)存地址
x=1111 # 執(zhí)行了x=1111,就把當(dāng)前空間的x指向了1111, 因此不指定模塊前綴容易混淆
import vs from ... import
在code.py import 導(dǎo)入模塊foo時(shí), code.py調(diào)用foo.x, 那么x指向了foo名稱空間中的x, 是和foo中的x要內(nèi)存地址
而如果是from foo import x, 那么在code.py中, x就直接指向了foo中x的內(nèi)存地址, 無需和x查詢, 也就導(dǎo)致了, 直接修改foo中的x的內(nèi)存地址, 是不會(huì)影響code.py中的x的內(nèi)存地址的, 除非重新from導(dǎo)入一次foo模塊
一行導(dǎo)入多個(gè)名字(不推薦)
from foo import x,get,change
*:導(dǎo)入模塊中的所有名字(一般不推薦, 除非要導(dǎo)入的名字太多
name='admin'
from foo import *
print(name) # 此時(shí), foo中如果有name, 那么name就指向了foo中name的內(nèi)存地址了, 原先name會(huì)被覆蓋, 容易造成混淆
每個(gè)模塊都內(nèi)置了__all__變量=[模塊中的每個(gè)名字組成的列表], 因此, from ... import *時(shí)會(huì)導(dǎo)入模塊中所有的名字
也就是說, __all__=[名字1,名字2,]可以用來控制, from ... import *時(shí), 會(huì)導(dǎo)入的名字
可以把*代表的名字寫到__all__里, 算上避免混淆的一個(gè)方法
from foo import get as g
print(g)
6. 模塊查找優(yōu)先級(jí)
無論是import還是from...import在導(dǎo)入模塊時(shí)都涉及到查找問題
優(yōu)先級(jí):
1、內(nèi)存(內(nèi)置模塊)
2、硬盤:按照sys.path中存放的目錄的順序依次查找要導(dǎo)入的模塊
import sys
值為一個(gè)列表,存放了一系列的文件夾
其中第一個(gè)文件夾是當(dāng)前執(zhí)行文件所在的文件夾
print(sys.path)
sys.modules查看已經(jīng)加載到內(nèi)存中的模塊
導(dǎo)入到內(nèi)存中的模塊, 只有在Python程序運(yùn)行結(jié)束后才會(huì)被回收, 否則無論是del還是在函數(shù)內(nèi)導(dǎo)入模塊運(yùn)行后都不會(huì)從內(nèi)存中釋放
6.1. sys.path的應(yīng)用
import sys
# 找foo.py就把foo.py的文件夾添加到環(huán)境變量中, 不過要注意這是程序運(yùn)行時(shí), 臨時(shí)修改環(huán)境變量
sys.path.append(r'foo.py在操作系統(tǒng)中存放的路徑') # 將存放路徑追加到sys.path的列表中
import foo
7. 包
7.1 包的介紹
1、包就是一個(gè)包含有__init__.py文件的文件夾
2、為何要有包
包的本質(zhì)是模塊的模塊的一種形式,包是用來被當(dāng)做模塊導(dǎo)入
導(dǎo)入包發(fā)生的事情:
1、產(chǎn)生一個(gè)名稱空間
2、運(yùn)行包下的__init__.py文件,將運(yùn)行過程中產(chǎn)生的名字都丟到1的名稱空間中
3、在當(dāng)前執(zhí)行文件的名稱空間中拿到一個(gè)名字mmm,mmm指向1的名稱空間
import mmm
print(mmm.x)
print(mmm.y)
mmm.say()
from mmm import x
7.2 包的絕對(duì)導(dǎo)入
環(huán)境變量是以執(zhí)行文件為準(zhǔn)備的,所有的被導(dǎo)入的模塊或者說后續(xù)的其他文件引用
的sys.path都是參照?qǐng)?zhí)行文件的sys.path
包為foo
存放三個(gè)模塊文件

圖片.png
如何在程序中調(diào)用?
需要在__init__.py中導(dǎo)入包foo中的模塊
__init__.py
# 導(dǎo)入包的方法
# 1 絕對(duì)導(dǎo)入: 以包的文件夾作為起始來進(jìn)行導(dǎo)入, 這里就是foo, 但前提是foo所在的目錄被加入到了sys.path環(huán)境變量, 否則找不動(dòng)foo就更找不到包里的模塊.
# 2 sys.path.append('foo所在路徑')
from foo.m1 import f1
強(qiáng)調(diào):
1.關(guān)于包相關(guān)的導(dǎo)入語句也分為import和from ... import ...
兩種,但是無論哪種,無論在什么位置,在導(dǎo)入時(shí)都必須遵循一個(gè)原則:
凡是在導(dǎo)入時(shí)帶點(diǎn)的,點(diǎn)的左邊都必須是一個(gè)包,否則非法。
可以帶有一連串的點(diǎn),如import 頂級(jí)包.子包.子模塊,但都必須遵循這個(gè)原則。但對(duì)于導(dǎo)入后,在使用時(shí)就沒有這種限制了,點(diǎn)的左邊可以是包,模塊,函數(shù),類(它們都可以用點(diǎn)的方式調(diào)用自己的屬性)。
例如:
from a.b.c.d.e.f import xxx
import a.b.c.d.e.f
其中a、b、c、d、e 都必須是包
2、包A和包B下有同名模塊也不會(huì)沖突,如A.a與B.a來自倆個(gè)命名空間
3、import導(dǎo)入文件時(shí),產(chǎn)生名稱空間中的名字來源于文件,
import 包,產(chǎn)生的名稱空間的名字同樣來源于文件,即包下的__init__.py,導(dǎo)入包本質(zhì)就是在導(dǎo)入該文件
7.3 包的相對(duì)導(dǎo)入
相對(duì)導(dǎo)入:僅限于包內(nèi)使用,不能跨出包(包內(nèi)模塊之間的導(dǎo)入,推薦使用相對(duì)導(dǎo)入)
.:代表當(dāng)前文件夾
..:代表上一層文件夾
from .m1 import f1
from .m2 import f2
from .m3 import f3
from .bbb.m4 import f4
強(qiáng)調(diào):
1:相對(duì)導(dǎo)入不能跨出包,所以相對(duì)導(dǎo)入僅限于包內(nèi)模板彼此之間
2:而絕對(duì)導(dǎo)入是沒有任何限制的,所以絕對(duì)導(dǎo)入是一種通用的導(dǎo)入方式