python模塊導(dǎo)入的細(xì)節(jié)詳解
python模塊導(dǎo)入細(xì)節(jié)
本文主要介紹了關(guān)于python模塊導(dǎo)入的相關(guān)內(nèi)容,分享出來供大家參考學(xué)習(xí),下面話不多說了,來一起看看詳細(xì)的介紹吧
官方手冊(cè):https://docs.python.org/3/tutorial/modules.html
可執(zhí)行文件和模塊
python源代碼文件按照功能可以分為兩種類型:
- 用于執(zhí)行的可執(zhí)行程序文件
- 不用與執(zhí)行,僅用于被其它python源碼文件導(dǎo)入的模塊文件
例如文件a.py和b.py在同一目錄下,它們的內(nèi)容分別是:
# b.py x="var x in module b" y=5 # a.py: import b import sys print(b.x) print(b.y)
a.py導(dǎo)入其它文件(b.py)后,就可以使用b.py文件中的屬性(如變量、函數(shù)等)。這里,a.py就是可執(zhí)行文件,b.py就是模塊文件,但模塊名為b,而非b.py。
python提供了一些標(biāo)準(zhǔn)庫(kù),是預(yù)定義好的模塊文件,例如上面的sys模塊。
在此有幾個(gè)注意點(diǎn),在后面會(huì)詳細(xì)解釋:
- 模塊b的文件名為b.py,但import導(dǎo)入的時(shí)候,使用的名稱為b,而非b.py
- a.py和b.py是在同一個(gè)目錄下的,如果不在同目錄下能否導(dǎo)入?
- 在a.py中訪問b.py模塊中的屬性時(shí),使用的是
b.x
、b.y
- 上面都是直接以模塊名導(dǎo)入的,python還支持更復(fù)雜的包導(dǎo)入方式,例如導(dǎo)入abc/b.py時(shí),使用
import abc.b
。下一篇文章會(huì)詳細(xì)解釋包的導(dǎo)入方式
python模塊搜索路徑
在a.py中導(dǎo)入模塊b的時(shí)候,python會(huì)做一系列的模塊文件路徑搜索操作:b.py在哪里?只有找到它才能讀取、運(yùn)行(裝載)該模塊。
在任何一個(gè)python程序啟動(dòng)時(shí),都會(huì)將模塊的搜索路徑收集到sys模塊的path屬性中(sys.path
)。當(dāng)python需要搜索模塊文件在何處時(shí),首先搜索內(nèi)置模塊,如果不是內(nèi)置模塊,則搜索sys.path中的路徑列表,搜索時(shí)會(huì)從該屬性列出的路徑中按照從前向后的順序進(jìn)行搜索,并且只要找到就立即停止搜索該模塊文件(也就是說不會(huì)后搜索的同名模塊覆蓋先搜索的同名模塊)。
例如,在a.py文件中輸出一下這個(gè)屬性的內(nèi)容:
# a.py: import sys print(sys.path)
結(jié)果:
['G:\\pycode', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']
python模塊的搜索路徑包括幾個(gè)方面,按照如下順序搜索:
- 程序文件(a.py)所在目錄,即
G:\\pycode
- 環(huán)境變量
PYTHONPATH
所設(shè)置的路徑(如果定義了該環(huán)境變量,則從左向右的順序搜索) - 標(biāo)準(zhǔn)庫(kù)路徑
- .pth文件中定義的路徑
需要注意,上面sys.path的結(jié)果中,除了.zip
是一個(gè)文件外,其它的搜索路徑全都是目錄,也就是從這些目錄中搜索模塊X的文件X.py是否存在。
程序所在目錄
這個(gè)目錄是最先搜索的,且是python自動(dòng)搜索的,無需對(duì)此進(jìn)行任何設(shè)置。從交互式python程序終輸出sys.path的結(jié)果:
>>> sys.path ['', 'C:\\WINDOWS\\system32', 'C:\\Program Files (x86)\\Python36-32\\Lib\\idlelib', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']
其中第一個(gè)''
表示的就是程序所在目錄。
注意程序所在目錄和當(dāng)前目錄是不同的。例如,在/tmp/目錄下執(zhí)行/pycode中的a.py文件
cd /tmp python /pycode/a.py
其中/tmp為當(dāng)前目錄,而/pycode是程序文件a.py所在的目錄。如果a.py中導(dǎo)入b.py,那么將首先搜索/pycode,而不是/tmp。
環(huán)境變量PYTHONPATH
這個(gè)變量中可以自定義一系列的模塊搜索路徑列表,這樣可以跨目錄搜索(另一種方式是設(shè)置.pth文件)。但默認(rèn)情況下這個(gè)環(huán)境變量是未設(shè)置的。
在windows下,設(shè)置PYTHONPATH環(huán)境變量的方式:命令行中輸入:SystemPropertiesAdvanced-->環(huán)境變量-->系統(tǒng)環(huán)境變量新建
如果是多個(gè)路徑,則使用英文格式的分號(hào)分隔。以下是臨時(shí)設(shè)置當(dāng)前命令行窗口的PYTHONPATH:
set PYTHONPATH='D:\pypath; d:\pypath1'
在unix下,設(shè)置PYTHONPATH環(huán)境變量的方式,使用冒號(hào)分隔多個(gè)路徑:另外,必須得export導(dǎo)出為環(huán)境變量
export PYTHONPATH=/tmp/pypath1:/tmp/pypath2
如果要永久生效,則寫入配置文件中:
echo 'export PYTHONPATH=/tmp/pypath1:/tmp/pypath2' >/etc/profile.d/pypth.sh chmod +x /etc/profile.d/pypth.sh source /etc/profile.d/pypth.sh
標(biāo)準(zhǔn)庫(kù)路徑
在Linux下,標(biāo)準(zhǔn)庫(kù)的路徑一般是在/usr/lib/pythonXXX/下(XXX表示python版本號(hào)),此目錄下有些分了子目錄。
例如:
['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']
其中/usr/lib/python3.5和其內(nèi)的幾個(gè)子目錄都是標(biāo)準(zhǔn)庫(kù)的搜索路徑。
注意其中/usr/lib/python35.zip,它是ZIP文件組件,當(dāng)定義此文件為搜索路徑時(shí),將自動(dòng)解壓縮該文件,并從此文件中搜索模塊。
Windows下根據(jù)python安裝位置的不同,標(biāo)準(zhǔn)庫(kù)的路徑不同。如果以默認(rèn)路徑方式安裝的python,則標(biāo)準(zhǔn)庫(kù)路徑為C:\\Program Files (x86)\\Python36-32及其分類的子目錄。
.pth文件自定義路徑
可以將自定義的搜索路徑放進(jìn)一個(gè).pth文件中,每行一個(gè)搜索路徑。然后將.pth文件放在python安裝目錄或某個(gè)標(biāo)準(zhǔn)庫(kù)路徑內(nèi)的sitepackages目錄下即可。
這是一種替換PYTHONPATH的友好方式,因?yàn)椴煌僮飨到y(tǒng)設(shè)置環(huán)境變量的方式不一樣,而以文件的方式記錄是所有操作系統(tǒng)都通用的。
例如,windows下,在python安裝目錄C:\\Program Files (x86)\\Python36-32
下新增一個(gè)mypath.pth文件,內(nèi)容如下:
d:\pypath1 d:\pypath2
再去輸出sys.path,將可以看到這兩個(gè)路徑已經(jīng)放進(jìn)了搜索列表中。
修改搜索路徑
除了上面環(huán)境變量和.pth文件,還可以直接修改sys.path或者site.getsitepackages()的結(jié)果。
例如,在import導(dǎo)入sys模塊之后,可以修改sys.path,向這個(gè)列表中添加其它搜索路徑,這樣之后導(dǎo)入其它模塊的時(shí)候,也會(huì)搜索該路徑。
例如:
import sys sys.path.append('d:\\pypath3') print(sys.path)
sys.path的最后一項(xiàng)將是新添加的路徑。
導(dǎo)入模塊的細(xì)節(jié)
導(dǎo)入模塊時(shí)的過程
python的import是在程序運(yùn)行期間執(zhí)行的,并非像其它很多語(yǔ)言一樣是在編譯期間執(zhí)行。也就是說,import可以出現(xiàn)在任何地方,只有執(zhí)行到這個(gè)import行時(shí),才會(huì)執(zhí)行導(dǎo)入操作。且在import某個(gè)模塊之前,無法訪問這個(gè)模塊的屬性。
python在import導(dǎo)入模塊時(shí),首先搜索模塊的路徑,然后編譯并執(zhí)行這個(gè)模塊文件。雖然概括起來只有兩個(gè)過程,但實(shí)際上很復(fù)雜。
前文已經(jīng)解釋了import的模塊搜索過程,所以這里大概介紹import的其它細(xì)節(jié)。
以前面的a.py中導(dǎo)入模塊文件b.py為例:
import b
import導(dǎo)入模塊時(shí),搜索到模塊文件b.py后:
1.首先在內(nèi)存中為每個(gè)待導(dǎo)入的模塊構(gòu)建module類的實(shí)例:模塊對(duì)象。這個(gè)模塊對(duì)象目前是空對(duì)象,這個(gè)對(duì)象的名稱為全局變量b。
注意細(xì)節(jié):module類的對(duì)象,變量b。
輸出下它們就知道:
print(b) print(type(b))
輸出結(jié)果:
<module 'b' from 'g:\\pycode\\b.py'>
<class 'module'>
因?yàn)閎是全局變量,所以當(dāng)前程序文件a.py中不能重新對(duì)全局變量b進(jìn)行賦值,這會(huì)使導(dǎo)入的模塊b被丟棄。例如,下面是錯(cuò)誤的:
import b b=3 print(b.x) # 已經(jīng)沒有模塊b了
另外,因?yàn)閕mport導(dǎo)入時(shí)是將模塊對(duì)象賦值給模塊變量,所以模塊變量名不能是python中的一些關(guān)鍵字,比如if、for等,這時(shí)會(huì)報(bào)錯(cuò)。雖然模塊文件名可以為list、keys等這樣的內(nèi)置函數(shù)名,但這會(huì)導(dǎo)致這些內(nèi)置函數(shù)不可用,因?yàn)楦鶕?jù)變量查找的作用域規(guī)則,首先查找全局變量,再查找內(nèi)置作用域。也就是說,模塊文件的文件名不能是這些關(guān)鍵字、也不應(yīng)該是這些內(nèi)置函數(shù)名。
File "g:/pycode/new.py", line 11 import if ^ SyntaxError: invalid syntax
2.構(gòu)造空模塊實(shí)例后,將編譯、執(zhí)行模塊文件b.py,并按照一定的規(guī)則將一些結(jié)果放進(jìn)這個(gè)模塊對(duì)象中。
注意細(xì)節(jié),編譯、執(zhí)行b.py、將結(jié)果保存到模塊對(duì)象中。
模塊第一次被導(dǎo)入的時(shí)候,會(huì)進(jìn)行編譯,并生成.pyc字節(jié)碼文件,然后python執(zhí)行這個(gè)pyc文件。當(dāng)模塊被再次導(dǎo)入時(shí),如果檢查到pyc文件的存在,且和源代碼文件的上一次修改時(shí)間戳mtime完全對(duì)應(yīng)(也就是說,編譯后源代碼沒有進(jìn)行過修改),則直接裝載這個(gè)pyc文件并執(zhí)行,不會(huì)再進(jìn)行額外的編譯過程。當(dāng)然,如果修改過源代碼,將會(huì)重新編譯得到新的pyc文件。
注意,并非所有的py文件都會(huì)生成編譯得到的pyc文件,對(duì)于那些只執(zhí)行一次的程序文件,會(huì)將內(nèi)存中的編譯結(jié)果在執(zhí)行完成后直接丟棄(多數(shù)時(shí)候如此,但仍有例外,比如使用compileall模塊可以強(qiáng)制編譯成pyc文件),但模塊會(huì)將內(nèi)存中的編譯結(jié)果持久化到pyc文件中。另外,運(yùn)行字節(jié)碼pyc文件并不會(huì)比直接運(yùn)行py文件更快,執(zhí)行它也一樣是一行行地解釋、執(zhí)行,唯一快的地方在于導(dǎo)入裝載的時(shí)候無需重新編譯而已。
執(zhí)行模塊文件(已完成編譯)的時(shí)候,按照一般的執(zhí)行流程執(zhí)行:一行一行地、以代碼塊為單元執(zhí)行。一般地,模塊文件中只用來聲明變量、函數(shù)等屬性,以便提供給導(dǎo)入它的模塊使用,而不應(yīng)該有其他任何操作性的行為,比如print()操作不應(yīng)該出現(xiàn)在模塊文件中,但這并非強(qiáng)制。
總之,執(zhí)行完模塊文件后,這個(gè)模塊文件將有一個(gè)自己的全局名稱空間,在此模塊文件中定義的變量、函數(shù)等屬性,都會(huì)記錄在此名稱空間中。
最后,模塊的這些屬性都會(huì)保存到模塊對(duì)象中。由于這個(gè)模塊對(duì)象賦值給了模塊變量b,所以通過變量b可以訪問到這個(gè)對(duì)象中的屬性(比如變量、函數(shù)等),也就是模塊文件內(nèi)定義的全局屬性。
只導(dǎo)入一次
假設(shè)a.py中導(dǎo)入了模塊b和模塊sys,在b.py中也導(dǎo)入了模塊sys,但python默認(rèn)對(duì)某個(gè)模塊只會(huì)導(dǎo)入一次,如果a.py中先導(dǎo)入sys,再導(dǎo)入b,那么導(dǎo)入b并執(zhí)行b.py的時(shí)候,會(huì)發(fā)現(xiàn)sys已經(jīng)導(dǎo)入了,不會(huì)再去導(dǎo)入sys。
實(shí)際上,python執(zhí)行程序的時(shí)候,會(huì)將所有已經(jīng)導(dǎo)入的模塊放進(jìn)sys.module屬性中,這是一個(gè)dict,可以通過下面的方式查看已導(dǎo)入的模塊名:
>>> import sys >>> list(sys.module.keys())
如果某個(gè)程序文件中多次使用import(或from)導(dǎo)入同一個(gè)模塊,雖然不會(huì)報(bào)錯(cuò),但實(shí)際上還是直接使用內(nèi)存中已裝載好的模塊對(duì)象。
例如,b.py中x=3,導(dǎo)入它之后修改該值,然后再次導(dǎo)入,發(fā)現(xiàn)b.x并不會(huì)發(fā)生改變:
import b print(b.x) # 3 b.x=33 print(b.x) # 33 import b print(b.x) # 33
但是python提供了reload進(jìn)行多次重復(fù)導(dǎo)入的方法,見后文。
使用別名
import導(dǎo)入時(shí),可以使用as
關(guān)鍵字指定一個(gè)別名作為模塊對(duì)象的變量,例如:
import b as bb bb.x=3 print(bb.x)
這時(shí)候模塊對(duì)象將賦值給變量bb,而不是b,b此時(shí)不再是模塊對(duì)象變量,而僅僅只是模塊名。使用別名并不會(huì)影響性能,因?yàn)樗鼉H僅只是一個(gè)賦值過程,只不過是從原來的賦值對(duì)象變量b變?yōu)樽兞縝b而已。
from導(dǎo)入部分屬性
import語(yǔ)句是導(dǎo)入模塊中的所有屬性,并且訪問時(shí)需要使用模塊變量來引用。例如:
import b print(b.x)
除了import,還有一個(gè)from語(yǔ)句,表示從模塊中導(dǎo)入部分指定的屬性,且使得可以直接使用這些屬性的名稱來引用這些屬性,而不需要加上模塊變量名。例如原來import導(dǎo)入時(shí)訪問變量x使用b.x
,from導(dǎo)入時(shí)只需使用x即可。實(shí)際上,from導(dǎo)入更應(yīng)該稱為屬性的再次賦值(拷貝)。
例如,b.py中定義了變量x、y、z,同時(shí)定義了函數(shù)f()和g(),在a.py中導(dǎo)入這個(gè)模塊文件,但只導(dǎo)入x變量和f函數(shù):
# a.py文件內(nèi)容: from b import x,f print(x) f() # b.py文件內(nèi)容: x=3 y=4 z=5 def f(): print("function f in b.py") def g(): print("function g in b.py")
注意上面a.py中引用模塊b中屬性的方式?jīng)]有加上b.X
,而是直接使用x和f()來引用。這和import是不一樣的。至于from和import導(dǎo)入時(shí)的變量名稱細(xì)節(jié),在下面的內(nèi)容中會(huì)詳細(xì)解釋。
雖然from語(yǔ)句只導(dǎo)入模塊的部分屬性,但實(shí)際上仍然會(huì)完整地執(zhí)行整個(gè)模塊文件。
同樣的,from語(yǔ)句也可以指定導(dǎo)入屬性的變量別名,例如,將b.py中的屬性x賦值給xx,將y賦值給yy:
from b import x as xx,y as yy print(xx) print(yy)
from語(yǔ)句還有一個(gè)特殊導(dǎo)入統(tǒng)配符號(hào)*
,它表示導(dǎo)入模塊中的所有屬性。
# a.py文件: from b import * print(x,y,z) f() g()
多數(shù)時(shí)候,不應(yīng)該使用from *
的方式,因?yàn)槲覀兛赡軙?huì)忘記某個(gè)模塊中有哪些屬性拷貝到了當(dāng)前文件,特別是多個(gè)from *
時(shí)可能會(huì)出現(xiàn)屬性覆蓋的問題。
重載模塊:imp.reload()
無論時(shí)import還是from,都只導(dǎo)入一次模塊,但使用reload()可以強(qiáng)制重新裝載模塊。
reload()是imp模塊中的一個(gè)函數(shù),所以要使用imp.reload()之前,必須先導(dǎo)入imp。
from imp import reload reload(b)
reload()是一個(gè)函數(shù),它的參數(shù)是一個(gè)已經(jīng)成功被導(dǎo)入過的模塊變量(如果使用了別名,則應(yīng)該使用別名作為reload的參數(shù)),也就是說該模塊必須在內(nèi)存中已經(jīng)有自己的模塊對(duì)象。
reload()會(huì)重新執(zhí)行模塊文件,并將執(zhí)行得到的屬性完全覆蓋到原有的模塊對(duì)象中。也就是說,reload()會(huì)重新執(zhí)行模塊文件,但不會(huì)在內(nèi)存中建立新的模塊對(duì)象,所以原有模塊對(duì)象中的屬性可能會(huì)被修改。
例如,模塊文件b.py中x=3,導(dǎo)入b模塊,修改其值為33,然后reload這個(gè)模塊,會(huì)發(fā)現(xiàn)值重新變回了3。
import b print(b.x) # 3 b.x=33 print(b.x) # 33 from imp import reload reload(b) print(b.x) # 3
有時(shí)候reload()很有用,可以讓程序無需重啟就執(zhí)行新的代碼。例如,在python的交互式模式下導(dǎo)入模塊b,然后修改python源碼,再reload導(dǎo)入:
>>> import b >>> b.x 3 # 不要關(guān)掉交互式解釋器,直接修改源代碼中的b=3333 >>> from imp import reload >>> reload(b) <module 'b' from 'G:\\pycode\\b.py'> >>> b.x 3333
但正因?yàn)閞eload()重載模塊會(huì)改變?cè)嫉闹担@可能是很危險(xiǎn)的行為,一定要清楚地知道它是在干什么。
導(dǎo)入模塊時(shí)的變量名稱細(xì)節(jié)
import導(dǎo)入的變量
import導(dǎo)入時(shí),模塊對(duì)象中的屬性有自己的名稱空間,然后將整個(gè)模塊對(duì)象賦值給模塊變量。
例如,在a.py中導(dǎo)入b:
import b print(b.x)
這個(gè)過程唯一和當(dāng)前文件a.py作用域有關(guān)的就是模塊對(duì)象變量b,b.py中聲明的屬性和當(dāng)前文件無任何關(guān)系。無論是訪問還是修改,都是直接修改這個(gè)模塊對(duì)象自身作用域中的值。所以,只要模塊變量b不出現(xiàn)沖突問題,可以放心地修改模塊b中的屬性。
另一方面,因?yàn)槊總€(gè)進(jìn)程都有自己的內(nèi)存空間,所以在a.py、c.py中都導(dǎo)入b時(shí),a.py中修改b的屬性值不會(huì)影響c.py中導(dǎo)入的屬性,a.py和c.py中模塊對(duì)象所保存的屬性都是執(zhí)行b.py后得到的,它們相互獨(dú)立。
from導(dǎo)入的變量
from導(dǎo)入模塊時(shí),會(huì)先執(zhí)行完模塊文件,然后將指定的部分屬性重新賦值給當(dāng)前程序文件的同名全局變量。
例如,在模塊文件b.py中定義了x、y、z變量和f()、g()函數(shù):
# b.py: x=3 y=4 b=5 def f(): print("function f in b.py") def g(): print("function g in b.py")
當(dāng)在a.py中導(dǎo)入b模塊時(shí),如果只導(dǎo)入x、y和f():
# a.py: from b import x, y, f
實(shí)際上的行為是構(gòu)造模塊對(duì)象后,將這個(gè)模塊對(duì)象對(duì)應(yīng)的名稱空間中的屬性x、y和f重新賦值給a.py中的變量x、y和f,然后丟棄整個(gè)模塊對(duì)象以及整個(gè)名稱空間。換句話說,b不再是一個(gè)有效的模塊變量(所以和import不一樣),來自b的x,y,z,f和g也都被丟棄。
這里有幾個(gè)細(xì)節(jié),需要詳細(xì)解釋清楚,只有理解了才能搞清楚它們是怎么生效的。
假設(shè)現(xiàn)在模塊文件b.py的內(nèi)容為,并且a.py中導(dǎo)入x,y,f屬性:
# b.py: x=3 y=[1,2] z=5 def f(): print("function f in b.py") def g(): print("function g in b.py") # a.py: from b import x,y,f
首先在執(zhí)行模塊文件b.py時(shí),會(huì)構(gòu)造好自己的模塊對(duì)象,并且模塊對(duì)象有自己的名稱空間(作用域),模塊對(duì)象構(gòu)造完成后,它的名稱空間大致如下:
然后python會(huì)在a.py的全局作用域內(nèi)創(chuàng)建和導(dǎo)入屬性同名的全局變量x,y和f,并且通過賦值的方式將模塊的屬性賦值給這些全局變量,也就是:
x = b.x y = b.y f = b.f
上面的b只是用來演示,實(shí)際上變量b是不存在的。
賦值完成后,我們和構(gòu)造的整個(gè)模塊對(duì)象就失去聯(lián)系了,因?yàn)闆]有變量b去引用這個(gè)對(duì)象。但需要注意,這個(gè)對(duì)象并沒有被刪除,僅僅只是我們無法通過b去找到它。
所以,現(xiàn)在的示意圖如下:
因?yàn)槭琴x值的方式傳值的,所以在a.py中修改這幾個(gè)變量的值時(shí),是直接在模塊對(duì)象作用域內(nèi)修改的:對(duì)于不可變對(duì)象,將在此作用域內(nèi)創(chuàng)建新對(duì)象,對(duì)于可變對(duì)象,將直接修改原始對(duì)象的值。
另一方面,由于模塊對(duì)象一直保留在內(nèi)存中,下次繼續(xù)導(dǎo)入時(shí),將直接使用該模塊對(duì)象。對(duì)于import和from,是直接使用該已存在的模塊對(duì)象,對(duì)于reload,是覆蓋此模塊對(duì)象。
例如,在a.py中修改不可變對(duì)象x和可變對(duì)象y,之后import或from時(shí),可變對(duì)象的值都會(huì)隨之改變,因?yàn)樗鼈兪褂玫亩际窃瓉淼哪K對(duì)象:
from b import x,y x=33 y[0]=333 from b import x,y print((x,y)) # 輸出(3, [333, 2]) import b print((b.x,b.y)) # 輸出(3, [333, 2])
from導(dǎo)入時(shí),由于b不再是模塊變量,所以無法再使用reload(b)去重載對(duì)象。如果想要重載,只能先import,再reload:
from b import x,y ...CODE... # 想要重載b import b from imp import reload reload(b)
查看模塊中的屬性
內(nèi)置函數(shù)dir可用于列出某模塊中定義了哪些屬性(全局名稱空間)。完整的說明見help(dir)
。
import b dir(b)
輸出結(jié)果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'g', 'x', 'y', 'z']
可見,模塊的屬性中除了自己定義的屬性外,還有一些內(nèi)置的屬性,比如上面以__
開頭和結(jié)尾的屬性。
如果dir()不給任何參數(shù),則輸出當(dāng)前環(huán)境下定義的名稱屬性:
>>> import b >>> x=3 >>> aaa=333 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'aaa', 'b', 'x']
每個(gè)屬性都對(duì)應(yīng)一個(gè)對(duì)象,例如x對(duì)應(yīng)的是int對(duì)象,b對(duì)應(yīng)的是module對(duì)象:
>>> type(x) <class 'int'> >>> type(b) <class 'module'>
既然是對(duì)象,那么它們都會(huì)有自己的屬性。例如:
>>> dir(x) ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
所以,也可以直接dir某個(gè)模塊內(nèi)的屬性:
import b dir(b.x) dir(b.__name__)
dir()不會(huì)列出內(nèi)置的函數(shù)和變量,如果想要輸出內(nèi)置的函數(shù)和變量,可以去標(biāo)準(zhǔn)模塊builtins中查看,因?yàn)樗鼈兌x在此模塊中:
import builtins dir(buildins)
除了內(nèi)置dir()函數(shù)可以獲取屬性列表(名稱空間),對(duì)象的__dict__
屬性也可以獲取對(duì)象的屬性字典(名稱空間),它們的結(jié)果不完全一樣。詳細(xì)說明參見dir()和__dict__屬性區(qū)別。
總的來說,獲取對(duì)象M中一個(gè)自定義的屬性age,有以下幾種方法:
M.age M.__dict__['age'] sys.modules['M'].age getattr(M,'age')
有妙用的__name__屬性
前面說了,py文件分兩種:用于執(zhí)行的程序文件和用于導(dǎo)入的模塊文件。當(dāng)直接使用python a.py
的時(shí)候表示a.py是用于執(zhí)行的程序文件,通過import/from方式導(dǎo)入的py文件是模塊文件。
__name__
屬性用來區(qū)分py文件是程序文件還是模塊文件:
- 當(dāng)文件是程序文件的時(shí)候,該屬性被設(shè)置為
__main__
- 當(dāng)文件是模塊文件的時(shí)候(也就是被導(dǎo)入時(shí)),該屬性被設(shè)置為自身模塊名
換句話說,__main__
表示的是當(dāng)前執(zhí)行程序文件的默認(rèn)模塊名,想必學(xué)過其他支持包功能的語(yǔ)言的人很容易理解:程序都需要一個(gè)入口,入口程序所在的包就是main包,在main包中導(dǎo)入其它包來組織整個(gè)程序。python也是如此,只不過它是隱式自動(dòng)設(shè)置的。
對(duì)于python來說,因?yàn)殡[式自動(dòng)設(shè)置,該屬性就有了特殊妙用:直接在模塊文件中通過if __name__ == "__main__"
來判斷,然后寫屬于執(zhí)行程序的代碼,如果直接用python執(zhí)行這個(gè)文件,說明這個(gè)文件是程序文件,于是會(huì)執(zhí)行屬于if代碼塊的代碼,如果是被導(dǎo)入,則是模塊文件,if代碼塊中的代碼不會(huì)被執(zhí)行。
顯然,這是python中非常方便的單元測(cè)試方式。
例如,寫一個(gè)模塊文件,里面包含一個(gè)函數(shù),用來求給定序列的最大值和最小值:
def minmax(func,*args): res = args[0] for arg in args[1:]: if func(arg,res): res = arg return res def lessthan(x,y): return x < y def greatethan(x,y): return x > y # 測(cè)試代碼 if __name__ == "__main__": print(minmax(lessthan,3,6,2,1,4,5)) print(minmax(greatethan,3,6,2,1,4,5))
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Django ModelSerializer實(shí)現(xiàn)自定義驗(yàn)證的使用示例
本文主要介紹了Django ModelSerializer實(shí)現(xiàn)自定義驗(yàn)證的使用示例,多種字段驗(yàn)證器幫助開發(fā)者確保數(shù)據(jù)的完整性和準(zhǔn)確性,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11python神經(jīng)網(wǎng)絡(luò)ResNet50模型的復(fù)現(xiàn)詳解
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)ResNet50模型的復(fù)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Python Pandas數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單介紹
這篇文章主要介紹了Python Pandas數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單介紹的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07決策樹剪枝算法的python實(shí)現(xiàn)方法詳解
這篇文章主要介紹了決策樹剪枝算法的python實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了決策樹剪枝算法的概念、原理并結(jié)合實(shí)例形式分析了Python相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-09-09python實(shí)現(xiàn)的解析crontab配置文件代碼
這篇文章主要介紹了python實(shí)現(xiàn)的解析crontab配置文件代碼,也可以說是python版的crontab,代碼中包含大量注釋,需要的朋友可以參考下2014-06-06Python實(shí)現(xiàn)CAN報(bào)文轉(zhuǎn)換工具教程
這篇文章主要介紹了Python實(shí)現(xiàn)CAN報(bào)文轉(zhuǎn)換工具教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05