Python函數(shù)屬性和PyC詳解
函數(shù)屬性
python中的函數(shù)是一種對(duì)象,它有屬于對(duì)象的屬性。除此之外,函數(shù)還可以自定義自己的屬性。注意,屬性是和對(duì)象相關(guān)的,和作用域無關(guān)。
自定義屬性
自定義函數(shù)自己的屬性方式很簡(jiǎn)單。假設(shè)函數(shù)名稱為myfunc,那么為這個(gè)函數(shù)添加一個(gè)屬性var1:
myfunc.var1="abc"
那么這個(gè)屬性var1就像是全局變量一樣被訪問、修改。但它并不是全局變量。
可以跨模塊自定義函數(shù)的屬性。例如,在b.py中有一個(gè)函數(shù)b_func(),然后在a.py中導(dǎo)入這個(gè)b.py模塊,可以直接在a.py中設(shè)置并訪問來自b.py中的b_func()的屬性。
import b b.b_func.var1="hello" print(b.b_func.var1) # 輸出hello
查看函數(shù)對(duì)象屬性
python函數(shù)是一種對(duì)象,是對(duì)象就會(huì)有對(duì)象的屬性??梢酝ㄟ^如下方式查看函數(shù)對(duì)象的屬性:
dir(func_name)
例如,有一個(gè)屬性__name__
,它表示函數(shù)的名稱:
def f(x): y=10 def g(z): return x+y+z return g print(f.__name__) # 輸出f
還有一個(gè)屬性__code__
,這個(gè)屬性是本文的重點(diǎn),它表示函數(shù)代碼對(duì)象:
print(f.__code__)
輸出:
<code object f at 0x0335B180, file "a.py", line 2>
上面的輸出結(jié)果已經(jīng)指明了__code__
也是對(duì)象,既然是對(duì)象,它就有自己的屬性:
print( dir(f.__code__) )
現(xiàn)在,就可以看到函數(shù)代碼對(duì)象相關(guān)的屬性,其中有一類屬性都以co_開頭,表示字節(jié)碼的意思,后文會(huì)詳細(xì)解釋這些屬性的意義。實(shí)際上,并非只有函數(shù)具有這些屬性,所有的代碼塊(code block)都有這些屬性。
[...省略其它非co_屬性... 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
如何查看這些__code__
的屬性?使用f.__code__.co_XXX
即可。由于dir()
返回的是屬性列表,所以下面使用循環(huán)將co_開頭的屬性都輸出出來:
for i in dir(f.__code__): if i.startswith("co"): print(i+":",eval("f.__code__."+i))
輸出結(jié)果:
co_argcount: 1 co_cellvars: ('x', 'y') co_code: b'd\x01\x89\x01\x87\x00\x87\x01f\x02d\x02d\x03\x84\x08}\x01|\x01S\x00' co_consts: (None, 10, <code object g at 0x02FB7338, file "g:/pycode/b.py", line 3>, 'f.<locals>.g') co_filename: g:/pycode/b.py co_firstlineno: 1 co_flags: 3 co_freevars: () co_kwonlyargcount: 0 co_lnotab: b'\x00\x01\x04\x01\x0e\x02' co_name: f co_names: () co_nlocals: 2 co_stacksize: 3 co_varnames: ('x', 'g')
此外,還可以使用dis模塊的show_code()函數(shù)來輸出這些信息的整理:
import dis def f(x): y=10 def g(z): return x+y+z return g print(dis.show_code(f))
輸出結(jié)果:
Name: f Filename: g:/pycode/b.py Argument count: 1 Kw-only arguments: 0 Number of locals: 2 Stack size: 3 Flags: OPTIMIZED, NEWLOCALS Constants: 0: None 1: 10 2: <code object g at 0x00A89338, file "g:/pycode/b.py", line 4> 3: 'f.<locals>.g' Variable names: 0: x 1: g Cell variables: 0: x 1: y None
__code__屬性的解釋
這些屬性定義在python源碼包的Include/code.h
文件中,如有需要,可自行去查看。
另外,這些屬性是代碼塊(code block)的,不限于函數(shù)。但此處以函數(shù)為例進(jìn)行說明。
由于這些屬性中涉及到了閉包屬性(或者嵌套函數(shù)的屬性),所以以下面這個(gè)a.py文件中的嵌套函數(shù)為例:
import dis x=3 def f(a,b,*args,c): a=3 y=10 print(a,b,c,x,y) def g(z): return a+b+c+x+z return g
以下是查看函數(shù)f()和閉包函數(shù)g()的方式:
# f()的show_code結(jié)果 dis.show_code(f) # f()的co_XXX屬性 for i in dir(f.__code__): if i.startswith("co"): print(i+":",eval("f.__code__."+i)) # 閉包函數(shù),注意,傳遞了*args參數(shù) f1=f(3,4,"arg1","arg2",c=5) # f1()的show_code結(jié)果 dis.show_code(f1) # f1()的co_XXX屬性 for i in dir(f1.__code__): if i.startswith("co"): print(i+":",eval("f1.__code__."+i))
下面將根據(jù)上面查看的結(jié)果解釋各屬性:
co_name
函數(shù)的名稱。
上例中該屬性的值為外層函數(shù)f和閉包函數(shù)g,注意不是f1。
co_filename
函數(shù)定義在哪個(gè)文件名中。
上例中為a.py。
co_firstlineno
函數(shù)聲明語(yǔ)句在文件中的第幾行。即def關(guān)鍵字所在的行號(hào)。
上例中f()的行號(hào)為3,g()的行號(hào)為7。
co_consts
該函數(shù)中使用的常量有哪些。python中并沒有專門的常量概念,所有字面意義的數(shù)據(jù)都是常量。
以下是show_code()得到的f()中的常量:
Constants: 0: None 1: 3 2: 10 3: <code object g at 0x0326B7B0, file "a.py", line 7> 4: 'f.<locals>.g'
而內(nèi)層函數(shù)g()中沒有常量。
co_kwonlyargcount
keyword-only的參數(shù)個(gè)數(shù)。
f()的keyword-only的參數(shù)只有c,所以個(gè)數(shù)為1
g()中沒有keyword-only類的參數(shù),所以為0
co_argcount
除去*args
之外的變量總數(shù)。實(shí)際上是除去*和**所收集的參數(shù)以及keyword-only類的參數(shù)之后剩余的參數(shù)個(gè)數(shù)。換句話說,是*或**前面的位置參數(shù)個(gè)數(shù)。
f()中屬于此類參數(shù)的有a和b,所以co_argcount數(shù)值為2
g()中只有一個(gè)位置參數(shù),所以co_argcount數(shù)值為1
co_nlocals
co_varnames
本地變量個(gè)數(shù)和它們的名稱,變量名稱收集在元組中。
f()的本地變量個(gè)數(shù)為6,元組的內(nèi)容為:('a', 'b', 'c', 'args', 'y', 'g')
g()的本地變量個(gè)數(shù)為1,元組的內(nèi)容為:('z',)
co_stacksize
本段函數(shù)需要在棧空間評(píng)估的記錄個(gè)數(shù)。換句話說,就是棧空間個(gè)數(shù)。
這個(gè)怎么計(jì)算的,我也不知道。以下是本示例的結(jié)果:
f()的棧空間個(gè)數(shù)為6
g()的??臻g個(gè)數(shù)為2
co_names
函數(shù)中保存的名稱符號(hào),一般除了本地變量外,其它需要查找的變量(如其它文件中的函數(shù)名,全局變量等)都需要保存起來。
f()的co_names:
Names: 0: print 1: x
g()的co_names:
Names: 0: x
co_cellvars
co_freevars
這兩個(gè)屬性和嵌套函數(shù)(或者閉包有關(guān)),它們是互相對(duì)應(yīng)的,所以內(nèi)容完全相同,它們以元組形式存在。
co_cellvars
是外層函數(shù)的哪些本地變量被內(nèi)層函數(shù)所引用
co_freevars
是內(nèi)層函數(shù)引用了哪些外層函數(shù)的本地變量
對(duì)外層函數(shù)來說,co_freevars
一定是空元組,對(duì)內(nèi)層函數(shù)來說,co_cellvars則一定是空元組。
如果知道自由變量的概念,這個(gè)很容易理解。
f()的co_cellvars
內(nèi)容: ('a', 'b', 'c')
g()的co_freevars
內(nèi)容: ('a', 'b', 'c')
co_code
co_flags
co_lnotab
這3個(gè)屬性和python函數(shù)的源代碼編譯成字節(jié)碼有關(guān),本文不解釋它們。
屬性和字節(jié)碼對(duì)象PyCodeObject
對(duì)于python,通常都認(rèn)為它是一種解釋型語(yǔ)言。但實(shí)際上它在進(jìn)行解釋之前,會(huì)先進(jìn)行編譯,會(huì)將python源代碼編譯成python的字節(jié)碼(bytecode),然后在python virtual machine(PVM)中運(yùn)行這段字節(jié)碼,就像Java一樣。但是PVM相比JVM而言,要更"高級(jí)"一些,這個(gè)高級(jí)的意思和高級(jí)語(yǔ)言的意思一樣:離物理機(jī)(處理機(jī)器碼)的距離更遠(yuǎn),或者說要更加抽象。
源代碼被python編譯器編譯的結(jié)果會(huì)保存在內(nèi)存中一個(gè)名為PyCodeObject的對(duì)象中,當(dāng)需要運(yùn)行時(shí),python解釋器開始將其放進(jìn)PVM中解釋執(zhí)行,執(zhí)行完畢后解釋器會(huì)"根據(jù)需要"將這個(gè)編譯的結(jié)果對(duì)象持久化到二進(jìn)制文件*.pyc
中。下次如果再執(zhí)行,將首先從文件中加載(如果存在的話)。
所謂"根據(jù)需要"是指該py文件是否只運(yùn)行一次,如果不是,則寫入pyc文件。至少,對(duì)于那些模塊文件,都會(huì)生成pyc二進(jìn)制文件。另外,使用compileall模塊,可以強(qiáng)制讓py文件編譯后生成pyc文件。
但需要注意,pyc雖然是字節(jié)碼文件,但并不意味著比py文件執(zhí)行效率更高,它們是一樣的,都是一行行地讀取、解釋、執(zhí)行。pyc唯一比py快的地方在導(dǎo)入,因?yàn)樗鼰o需編譯的過程,而是直接從文件中加載對(duì)象。
py文件中的每一個(gè)代碼塊(code block)都有一個(gè)屬于自己的PyCodeObject對(duì)象。每個(gè)代碼塊除了被編譯得到的字節(jié)碼數(shù)據(jù),還包含這個(gè)代碼塊中的常量、變量、??臻g等內(nèi)容,也就是前面解釋的各種co_XXX屬性信息。
pyc文件包含3部分:
- 4字節(jié)的Magic int,表示pyc的版本信息
- 4字節(jié)的int,是pyc的產(chǎn)生時(shí)間,如果與py文件修改時(shí)間不同,則會(huì)重新生成
- PycodeObject對(duì)象序列化的內(nèi)容
參考文章://chabaoo.cn/article/225710.htm
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
python將天數(shù)轉(zhuǎn)換為日期字符串的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于python將天數(shù)轉(zhuǎn)換為日期字符串的相關(guān)資料,以及將將字符串的時(shí)間轉(zhuǎn)換為時(shí)間戳的實(shí)例代碼,需要的朋友可以參考下2022-01-01Python中defaultdict與lambda表達(dá)式用法實(shí)例小結(jié)
這篇文章主要介紹了Python中defaultdict與lambda表達(dá)式用法,結(jié)合實(shí)例形式分析了Python中defaultdict與lambda表達(dá)式的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2018-04-04python pycharm最新版本激活碼(永久有效)附python安裝教程
PyCharm是一個(gè)多功能的集成開發(fā)環(huán)境,只需要在pycharm中創(chuàng)建python file就運(yùn)行python,并且pycharm內(nèi)置完備的功能,這篇文章給大家介紹python pycharm激活碼最新版,需要的朋友跟隨小編一起看看吧2020-01-01python實(shí)現(xiàn)判斷一個(gè)字符串是否是合法IP地址的示例
今天小編就為大家分享一篇python實(shí)現(xiàn)判斷一個(gè)字符串是否是合法IP地址的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-06-06python實(shí)現(xiàn)Windows電腦定時(shí)關(guān)機(jī)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)Windows電腦定時(shí)關(guān)機(jī)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Python之urlencode和urldecode案例講解
這篇文章主要介紹了Python之urlencode和urldecode案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Python使用socket的UDP協(xié)議實(shí)現(xiàn)FTP文件服務(wù)功能
這篇文章主要介紹了Python使用socket的UDP協(xié)議實(shí)現(xiàn)FTP文件服務(wù),本示例主要是用Python的socket,使用UDP協(xié)議實(shí)現(xiàn)一個(gè)FTP服務(wù)端、FTP客戶端,用來實(shí)現(xiàn)文件的傳輸,需要的朋友可以參考下2023-10-10Python實(shí)現(xiàn)計(jì)算長(zhǎng)方形面積(帶參數(shù)函數(shù)demo)
今天小編就為大家分享一篇Python實(shí)現(xiàn)計(jì)算長(zhǎng)方形面積(帶參數(shù)函數(shù)demo),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01Python自動(dòng)化構(gòu)建工具scons使用入門筆記
這篇文章主要介紹了Python自動(dòng)化構(gòu)建工具scons使用入門筆記,本文講解了安裝scons、scons常用命令、scons使用示例等內(nèi)容,需要的朋友可以參考下2015-03-03