Python函式屬性和PyCodeObject
函式屬性
python中的函式是一種物件,它有屬於物件的屬性。除此之外,函式還可以自定義自己的屬性。注意,屬性是和物件相關的,和作用域無關。
自定義屬性
自定義函式自己的屬性方式很簡單。假設函式名稱為myfunc,那麼為這個函式新增一個屬性var1:
myfunc.var1="abc"
那麼這個屬性var1就像是全域性變數一樣被訪問、修改。但它並不是全域性變數。
可以跨模組自定義函式的屬性。例如,在b.py中有一個函式
b_func()
,然後在a.py中匯入這個b.py模組,可以直接在a.py中設定並訪問來自b.py中的
b_func()
的屬性。
import b b.b_func.var1="hello"print(b.b_func.var1) # 輸出hello
檢視函式物件屬性
python函式是一種物件,是物件就會有物件的屬性。可以透過如下方式檢視函式物件的屬性:
dir(func_name)
例如,有一個屬性
__name__
,它表示函式的名稱:
def f(x): y=10 def g(z): return x+y+z return g print(f.__name__) # 輸出f
還有一個屬性
__code__
,這個屬性是本文的重點,它表示函式程式碼物件:
print(f.__code__)
輸出:
<code object f at 0x0335B180, file "a.py", line 2>
上面的輸出結果已經指明瞭
__code__
也是物件,既然是物件,它就有自己的屬性:
print( dir(f.__code__) )
現在,就可以看到函式程式碼物件相關的屬性,其中有一類屬性都以
co_
開頭,表示位元組碼的意思,後文會詳細解釋這些屬性的意義。實際上,並非只有函式具有這些屬性,所有的程式碼塊(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()
返回的是屬性列表,所以下面使用迴圈將
co_
開頭的屬性都輸出出來:
for i in dir(f.__code__): if i.startswith("co"): print(i+":",eval("f.__code__."+i))
輸出結果:
co_argcount: 1co_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.pyco_firstlineno: 1co_flags: 3co_freevars: ()co_kwonlyargcount: 0co_lnotab: b'\x00\x01\x04\x01\x0e\x02'co_name: fco_names: ()co_nlocals: 2co_stacksize: 3co_varnames: ('x', 'g')
此外,還可以使用
dis
模組的
show_code()
函式來輸出這些資訊的整理:
import disdef f(x): y=10 def g(z): return x+y+z return g print(dis.show_code(f))
輸出結果:
Name: fFilename: g:/pycode/b.pyArgument count: 1 Kw-only arguments: 0 Number of locals: 2 Stack size: 3Flags: OPTIMIZED, NEWLOCALSConstants: 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)的,不限於函式 。但此處以函式為例進行說明。
由於這些屬性中涉及到了閉包屬性(或者巢狀函式的屬性),所以以下面這個a.py檔案中的巢狀函式為例:
import dis x=3def 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
以下是檢視函式f()和閉包函式g()的方式:
# f()的show_code結果dis.show_code(f)# f()的co_XXX屬性for i in dir(f.__code__): if i.startswith("co"): print(i+":",eval("f.__code__."+i))# 閉包函式,注意,傳遞了*args引數f1=f(3,4,"arg1","arg2",c=5)# f1()的show_code結果dis.show_code(f1)# f1()的co_XXX屬性for i in dir(f1.__code__): if i.startswith("co"): print(i+":",eval("f1.__code__."+i))
下面將根據上面檢視的結果解釋各屬性:
co_name
函式的名稱。
上例中該屬性的值為外層函式f和閉包函式g,注意不是f1。
co_filename
函式定義在哪個檔名中。
上例中為
a.py
。
co_firstlineno
函式宣告語句在檔案中的第幾行。即def關鍵字所在的行號。
上例中f()的行號為3,g()的行號為7。
co_consts
該函式中使用的常量有哪些。python中並沒有專門的常量概念,所有字面意義的資料都是常量。
以下是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'
而內層函式g()中沒有常量。
co_kwonlyargcount
keyword-only的引數個數。
f()的keyword-only的引數只有c,所以個數為1
g()中沒有keyword-only類的引數,所以為0
co_argcount
除去
*args
之外的變數總數。實際上是除去
*
和
**
所收集的引數以及keyword-only類的引數之後剩餘的引數個數。換句話說,是
*
或
**
前面的位置引數個數。
f()中屬於此類引數的有a和b,所以co_argcount數值為2
g()中只有一個位置引數,所以co_argcount數值為1
co_nlocals
co_varnames
本地變數
個數和它們的名稱,變數名稱收集在元組中。
f()的本地變數個數為6,元組的內容為:
('a', 'b', 'c', 'args', 'y', 'g')
g()的本地變數個數為1,元組的內容為:
('z',)
co_stacksize
本段函式需要在棧空間評估的記錄個數。換句話說,就是棧空間個數。
這個怎麼計算的,我也不知道。以下是本示例的結果:
f()的棧空間個數為6
g()的棧空間個數為2
co_names
函式中儲存的名稱符號,一般除了本地變數外,其它需要查詢的變數(如其它檔案中的函式名,全域性變數等)都需要儲存起來。
f()的co_names:
Names: 0: print 1: x
g()的co_names:
Names: 0: x
co_cellvars
co_freevars
這兩個屬性和巢狀函式(或者閉包有關),它們是互相對應的,所以內容完全相同,它們以元組形式存在。
co_cellvars
是外層函式的哪些本地變數被內層函式所引用
co_freevars
是內層函式引用了哪些外層函式的本地變數
對外層函式來說,
co_freevars
一定是空元組,對內層函式來說,
co_cellvars
則一定是空元組。
如果知道自由變數的概念,這個很容易理解。
f()的
co_cellvars
內容:
('a', 'b', 'c', 'y')
f()的
co_freevars
內容:
('a', 'b', 'c', 'y')
co_code
co_flags
co_lnotab
這3個屬性和python函式的原始碼編譯成位元組碼有關,本文不解釋它們。
屬性和位元組碼物件PyCodeObject
對於python,通常都認為它是一種解釋型語言。但實際上它在進行解釋之前,會先進行編譯,會將python原始碼編譯成python的位元組碼(bytecode),然後在python virtual machine(PVM)中執行這段位元組碼,就像Java一樣。但是PVM相比JVM而言,要更"高階"一些,這個高階的意思和高階語言的意思一樣:離物理機(處理機器碼)的距離更遠,或者說要更加抽象。
原始碼被python編譯器編譯的結果會儲存在記憶體中一個名為
PyCodeObject
的物件中,當需要執行時,python直譯器開始將其放進PVM中解釋執行,執行完畢後直譯器會"根據需要"將這個編譯的結果物件持久化到二進位制檔案
*.pyc
中。下次如果再執行,將首先從檔案中載入(如果存在的話)。
所謂"根據需要"是指該py檔案是否只執行一次,如果不是,則寫入pyc檔案。至少,對於那些模組檔案,都會生成pyc二進位制檔案。另外,使用compileall模組,可以強制讓py檔案編譯後生成pyc檔案。
但需要注意,pyc雖然是位元組碼檔案,但並不意味著比py檔案執行效率更高,它們是一樣的,都是一行行地讀取、解釋、執行。pyc唯一比py快的地方在匯入,因為它無需編譯的過程,而是直接從檔案中載入物件。
py檔案中的每一個程式碼塊(code block)都有一個屬於自己的PyCodeObject物件。每個程式碼塊除了被編譯得到的位元組碼資料,還包含這個程式碼塊中的常量、變數、棧空間等內容,也就是前面解釋的各種
co_XXX
屬性資訊。
pyc檔案包含3部分:
-
4位元組的Magic int,表示pyc的版本資訊
-
4位元組的int,是pyc的產生時間,如果與py檔案修改時間不同,則會重新生成
-
PycodeObject物件序列化的內容
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31557424/viewspace-2220664/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python三種屬性管理魔法函式Python函式
- JavaScript 函式 name 屬性JavaScript函式
- JavaScript 函式 length 屬性JavaScript函式
- jQuery屬性操作之.val()函式jQuery函式
- Python程式碼閱讀(第38篇):根據謂詞函式和屬性字串構造判斷函式Python函式字串
- Python:PyHook3的HookManager內建屬性以及相關函式PythonHook函式
- 會Python?請聽題那麼是.pyc和PyCodeObject?PythonObject
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- 15.3 極限函式與和函式性質函式
- Python的tkinter獲取元件屬性和設定元件屬性Python元件
- Python類屬性和例項屬性分別是什麼?Python
- 【Python】python map()函式和lambda表示式Python函式
- 【Kotlin】擴充套件屬性、擴充套件函式Kotlin套件函式
- 全面瞭解Vue3的 ref 和相關函式和計算屬性Vue函式
- defer 屬性和 async 屬性
- Python之operator.itemgetter函式和sorted函式Python函式
- Kotlin擴充套件函式與屬性原理解析Kotlin套件函式
- [BUG反饋]模型屬性自定義函式提交不了模型函式
- Python技法3:匿名函式、回撥函式和高階函式Python函式
- Python 訪問和設定私有屬性Python
- Python __dict__屬性:檢視物件內部所有屬性名和屬性值組成的字典Python物件
- 尤拉函式性質和模版函式
- 【Python基礎】Python 函式返回多個值和函式註解Python函式
- Python 類的屬性與例項屬性Python
- python物件屬性管理(2):property管理屬性Python物件
- 透過Lambda函式的方式獲取屬性名稱函式
- 03-python函式和列表Python函式
- Python的字典、集合和函式Python函式
- 平凡的函式 線性篩積性函式函式
- 打造屬於自己的underscore系列(五)- 偏函式和函式柯里化函式
- Python區域性函式及用法詳解Python函式
- Python中的屬性Python
- [譯] 為函式自定義屬性的八種實現方法函式
- render函式處理動態獲取img的src屬性函式
- C#訪問或修改私有類、函式、變數、屬性C#函式變數
- 屬性和方法
- Python - 物件導向程式設計 - 公共屬性、保護屬性、私有屬性Python物件程式設計
- Python 函式和變數作用域Python函式變數