19.python模塊

1. 什么是模塊

常見(jiàn)的場(chǎng)景:一個(gè)模塊就是一個(gè)包含了python定義和聲明的文件,文件名就是模塊名字加上.py的后綴。
但其實(shí)import加載的模塊分為四個(gè)通用類(lèi)別: 
  1 使用python編寫(xiě)的代碼(.py文件)
  2 已被編譯為共享庫(kù)或DLL的C或C++擴(kuò)展
  3 封裝好的一組模塊的包
  4 使用C編寫(xiě)并鏈接到python解釋器的內(nèi)置模塊


2. 為何要使用模塊

  1. 方便組織管理
  2. 方便代碼復(fù)用

3. 使用模塊

  1. 模塊可以包含可執(zhí)行的語(yǔ)句和函數(shù)的定義,這些語(yǔ)句的目的是初始化模塊,它們只在模塊名第一次遇到導(dǎo)入import語(yǔ)句時(shí)才執(zhí)行(import語(yǔ)句是可以在程序中的任意位置使用的,且針對(duì)同一個(gè)模塊可以import多次,為了防止你重復(fù)導(dǎo)入,python的優(yōu)化手段是:第一次導(dǎo)入后就將模塊名加載到內(nèi)存了,后續(xù)的import語(yǔ)句僅是對(duì)已經(jīng)加載大內(nèi)存中的模塊對(duì)象增加了一次引用,不會(huì)重新執(zhí)行模塊內(nèi)的語(yǔ)句)。
  2. 我們可以從sys.modules中找到當(dāng)前已經(jīng)加載的模塊,sys.modules是一個(gè)字典,內(nèi)部包含模塊名與模塊對(duì)象的映射,該字典決定了導(dǎo)入模塊時(shí)是否需要重新導(dǎo)入。
  3. 每個(gè)模塊都是一個(gè)獨(dú)立的名稱(chēng)空間,定義在這個(gè)模塊中的函數(shù),把這個(gè)模塊的名稱(chēng)空間當(dāng)做全局名稱(chēng)空間,這樣我們?cè)诰帉?xiě)自己的模塊時(shí),就不用擔(dān)心我們定義在自己模塊中全局變量會(huì)在被導(dǎo)入時(shí),與使用者的全局變量沖突

總結(jié)

首次導(dǎo)入模塊my_module時(shí)會(huì)做三件事:
1.為源文件(my_module模塊)創(chuàng)建新的名稱(chēng)空間,在my_module中定義的函數(shù)和方法若是使用到了global時(shí)訪問(wèn)的就是這個(gè)名稱(chēng)空間。

2.在新創(chuàng)建的命名空間中執(zhí)行模塊中包含的代碼,見(jiàn)初始導(dǎo)入import my_module

1 提示:導(dǎo)入模塊時(shí)到底執(zhí)行了什么?
In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table.
事實(shí)上函數(shù)定義也是“被執(zhí)行”的語(yǔ)句,模塊級(jí)別函數(shù)定義的執(zhí)行將函數(shù)名放入模塊全局名稱(chēng)空間表,用globals()可以查看

3.創(chuàng)建名字my_module來(lái)引用該命名空間

這個(gè)名字和變量名沒(méi)什么區(qū)別,都是‘第一類(lèi)的’,且使用my_module.名字的方式可以訪問(wèn)my_module.py文件中定義的名字,my_module.名字與test.py中的名字來(lái)自?xún)蓚€(gè)完全不同的地方。

示例一、 money與my_module.money不沖突

#測(cè)試一:money與my_module.money不沖突
#demo.py
import my_module
money=10
print(my_module.money)

'''
執(zhí)行結(jié)果:
from the my_module.py
1000
'''

示例二、read1與my_module.read1不沖突

#測(cè)試二:read1與my_module.read1不沖突
#demo.py
import my_module
def read1():
    print('========')
my_module.read1()

'''
執(zhí)行結(jié)果:
from the my_module.py
my_module->read1->money 1000
'''

示例三、執(zhí)行my_module.change()操作的全局變量money仍然是my_module中的

#測(cè)試三:執(zhí)行my_module.change()操作的全局變量money仍然是my_module中的
#demo.py
import my_module
money=1
my_module.change()
print(money)

'''
執(zhí)行結(jié)果:
from the my_module.py
1
'''

4. 為模塊名起別名,相當(dāng)于m1=1;m2=m1

示范用法一:
有兩中sql模塊mysql和oracle,根據(jù)用戶(hù)的輸入,選擇不同的sql功能

def sqlparse():
    print('from mysql sqlparse')
#oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse() 

5. import 與from...import 的區(qū)別

  1. 對(duì)比import my_module,會(huì)將源文件的名稱(chēng)空間'my_module'帶到當(dāng)前名稱(chēng)空間中,使用時(shí)必須是my_module.名字的方式
    而from 語(yǔ)句相當(dāng)于import,也會(huì)創(chuàng)建新的名稱(chēng)空間,但是將my_module中的名字直接導(dǎo)入到當(dāng)前的名稱(chēng)空間中,在當(dāng)前名稱(chēng)空間中,直接使用名字就可以了。

  2. 如果用from ... import ... 時(shí)導(dǎo)入的變量名在文件中有重名時(shí)會(huì)被覆蓋

  3. from my_module import * 把my_module中所有的不是以下劃線(_)開(kāi)頭的名字都導(dǎo)入到當(dāng)前位置。

4.如果在my_module.py中增加一行__all__=['money','read1'] #這樣在另外一個(gè)文件中用from my_module import *就這能導(dǎo)入列表中規(guī)定的兩個(gè)名字。

6. 模塊的加載與修改

考慮到性能的原因,每個(gè)模塊只被導(dǎo)入一次,放入字典sys.modules中,如果你改變了模塊的內(nèi)容,你必須重啟程序,python不支持重新加載或卸載之前導(dǎo)入的模塊,

有的同學(xué)可能會(huì)想到直接從sys.modules中刪除一個(gè)模塊不就可以卸載了嗎,注意了,你刪了sys.modules中的模塊對(duì)象仍然可能被其他程序的組件所引用,因而不會(huì)被清除。

特別的對(duì)于我們引用了這個(gè)模塊中的一個(gè)類(lèi),用這個(gè)類(lèi)產(chǎn)生了很多對(duì)象,因而這些對(duì)象都有關(guān)于這個(gè)模塊的引用。

如果只是你想交互測(cè)試的一個(gè)模塊,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這只能用于測(cè)試環(huán)境。

7. 把模塊當(dāng)做腳本執(zhí)行

我們可以通過(guò)模塊的全局變量name來(lái)查看模塊名:
當(dāng)做腳本運(yùn)行:
name 等于'main'

當(dāng)做模塊導(dǎo)入:
name= 模塊名

作用:用來(lái)控制.py文件在不同的應(yīng)用場(chǎng)景下執(zhí)行不同的邏輯

8. 模塊搜索路徑

python解釋器在啟動(dòng)時(shí)會(huì)自動(dòng)加載一些模塊,可以使用sys.modules查看

在第一次導(dǎo)入某個(gè)模塊時(shí)(比如my_module),會(huì)先檢查該模塊是否已經(jīng)被加載到內(nèi)存中(當(dāng)前執(zhí)行文件的名稱(chēng)空間對(duì)應(yīng)的內(nèi)存),如果有則直接引用

如果沒(méi)有,解釋器則會(huì)查找同名的內(nèi)建模塊,如果還沒(méi)有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。

所以總結(jié)模塊的查找順序是:內(nèi)存中已經(jīng)加載的模塊->內(nèi)置模塊->sys.path路徑中包含的模塊

sys.path的初始化的值來(lái)自于:

The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.

需要特別注意的是:我們自定義的模塊名不應(yīng)該與系統(tǒng)內(nèi)置模塊重名。雖然每次都說(shuō),但是仍然會(huì)有人不停的犯錯(cuò)。

在初始化后,python程序可以修改sys.path,路徑放到前面的優(yōu)先于標(biāo)準(zhǔn)庫(kù)被加載。

1 >>> import sys
2 >>> sys.path.append('/a/b/c/d')
3 >>> sys.path.insert(0,'/x/y/z') #排在前的目錄,優(yōu)先被搜索

注意:搜索時(shí)按照sys.path中從左到右的順序查找,位于前的優(yōu)先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會(huì)把.zip歸檔文件當(dāng)成一個(gè)目錄去處理。

#首先制作歸檔文件:zip module.zip foo.py bar.py

import sys
sys.path.append('module.zip')
import foo,bar

#也可以使用zip中目錄結(jié)構(gòu)的具體位置
sys.path.append('module.zip/lib/python')

#windows下的路徑不加r開(kāi)頭,會(huì)語(yǔ)法錯(cuò)誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')

至于.egg文件是由setuptools創(chuàng)建的包,這是按照第三方python庫(kù)和擴(kuò)展時(shí)使用的一種常見(jiàn)格式,.egg文件實(shí)際上只是添加了額外元數(shù)據(jù)(如版本號(hào),依賴(lài)項(xiàng)等)的.zip文件。

需要強(qiáng)調(diào)的一點(diǎn)是:只能從.zip文件中導(dǎo)入.py,.pyc等文件。使用C編寫(xiě)的共享庫(kù)和擴(kuò)展塊無(wú)法直接從.zip文件中加載(此時(shí)setuptools等打包系統(tǒng)有時(shí)能提供一種規(guī)避方法),且從.zip中加載文件不會(huì)創(chuàng)建.pyc或者.pyo文件,因此一定要事先創(chuàng)建他們,來(lái)避免加載模塊是性能下降。

9. 編譯python文件

為了提高加載模塊的速度,強(qiáng)調(diào)強(qiáng)調(diào)強(qiáng)調(diào):提高的是加載速度而絕非運(yùn)行速度。python解釋器會(huì)在__pycache__目錄中下緩存每個(gè)模塊編譯后的版本,格式為:module.version.pyc。通常會(huì)包含python的版本號(hào)。例如,在CPython3.3版本下,my_module.py模塊會(huì)被緩存成__pycache__/my_module.cpython-33.pyc。這種命名規(guī)范保證了編譯后的結(jié)果多版本共存。

Python檢查源文件的修改時(shí)間與編譯的版本進(jìn)行對(duì)比,如果過(guò)期就需要重新編譯。這是完全自動(dòng)的過(guò)程。并且編譯的模塊是平臺(tái)獨(dú)立的,所以相同的庫(kù)可以在不同的架構(gòu)的系統(tǒng)之間共享,即pyc使一種跨平臺(tái)的字節(jié)碼,類(lèi)似于JAVA和.NET,是由python虛擬機(jī)來(lái)執(zhí)行的,但是pyc的內(nèi)容跟python的版本相關(guān),不同的版本編譯后的pyc文件不同,2.5編譯的pyc文件不能到3.5上執(zhí)行,并且pyc文件是可以反編譯的,因而它的出現(xiàn)僅僅是用來(lái)提升模塊的加載速度的。

python解釋器在以下兩種情況下不檢測(cè)緩存
  1 如果是在命令行中被直接導(dǎo)入模塊,則按照這種方式,每次導(dǎo)入都會(huì)重新編譯,并且不會(huì)存儲(chǔ)編譯后的結(jié)果(python3.3以前的版本應(yīng)該是這樣)

python -m my_module.py

2 如果源文件不存在,那么緩存的結(jié)果也不會(huì)被使用,如果想在沒(méi)有源文件的情況下來(lái)使用編譯后的結(jié)果,則編譯后的結(jié)果必須在源目錄下

提示:
1.模塊名區(qū)分大小寫(xiě),foo.py與FOO.py代表的是兩個(gè)模塊
2.你可以使用-O或者-OO轉(zhuǎn)換python命令來(lái)減少編譯模塊的大小

-O轉(zhuǎn)換會(huì)幫你去掉assert語(yǔ)句
-OO轉(zhuǎn)換會(huì)幫你去掉assert語(yǔ)句和doc文檔字符串
由于一些程序可能依賴(lài)于assert語(yǔ)句或文檔字符串,你應(yīng)該在在確認(rèn)需要的情況下使用這些選項(xiàng)。

3.在速度上從.pyc文件中讀指令來(lái)執(zhí)行不會(huì)比從.py文件中讀指令執(zhí)行更快,只有在模塊被加載時(shí),.pyc文件才是更快的

4.只有使用import語(yǔ)句是才將文件自動(dòng)編譯為.pyc文件,在命令行或標(biāo)準(zhǔn)輸入中指定運(yùn)行腳本則不會(huì)生成這類(lèi)文件,因而我們可以使用compieall模塊為一個(gè)目錄中的所有模塊創(chuàng)建.pyc文件

模塊可以作為一個(gè)腳本(使用python -m compileall)編譯Python源

python -m compileall /module_directory 遞歸著編譯
如果使用python -O -m compileall /module_directory -l則只一層

命令行里使用compile()函數(shù)時(shí),自動(dòng)使用python -O -m compileall

詳見(jiàn):https://docs.python.org/3/library/compileall.html#module-compileall

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 模塊和包 一 模塊 1 什么是模塊? 常見(jiàn)的場(chǎng)景:一個(gè)模塊就是一個(gè)包含了python定義和聲明的文件,文件名就是...
    go以恒閱讀 2,344評(píng)論 0 4
  • 1. 認(rèn)識(shí)模塊 1.1 什么是模塊 一個(gè)模塊就是一個(gè)包含了python定義和聲明的文件,文件名就是模塊名字加上.p...
    hswangxun閱讀 693評(píng)論 0 1
  • If you quit from the Python interpreter and enter it agai...
    linyk3閱讀 460評(píng)論 0 0
  • 每天認(rèn)真洗臉,多讀書(shū),按時(shí)睡,少食多餐。變得溫柔,大度,繼續(xù)善良,保持愛(ài)心。不在人前矯情,四處訴說(shuō)以求寬慰,而是學(xué)...
    夜談_大豬蹄子閱讀 102評(píng)論 0 0
  • 在iOS中,單例模式是比較常用的一種模式,保證相關(guān)代碼只會(huì)執(zhí)行一次,但有時(shí)候會(huì)有一種場(chǎng)景,需要銷(xiāo)毀單例進(jìn)行重建。 ...
    ShanJiJi閱讀 3,197評(píng)論 0 3

友情鏈接更多精彩內(nèi)容