官網對inspect子產品的解釋是:
inspect子產品主要提供了四種用處:
- 對是否是子產品,架構,函數等進行類型檢查
- 擷取源碼
- 擷取類或函數的參數資訊
- 解析堆棧
說白了就是以下三大類:
- 檢查對象(isxxx)
- 擷取對象(getxxx)
- 解析堆棧對象執行個體
先說說解析堆棧對象執行個體,個人感覺這是最有用的
我們可以寫個Demo來模拟log日志列印
我們希望列印的時候實作以下需求:
- 列印變量值,也列印變量名
- 列印出是在哪個檔案,哪個方法,哪一行執行列印了
import inspect
def f2():
print(inspect.stack())
def f1():
f2()
def main():
f1()
if __name__ == "__main__":
main()
列印結果簡化後是這樣的:
[
FrameInfo(frame=<frame object at F398>, filename='D:/demo.py', lineno=5, function='v2', code_context=['print(inspect.stack())\n'], index=0),
FrameInfo(frame=<frame object at 1BA8>, filename='D:/demo.py', lineno=9, function='v1', code_context=['v2()\n'], index=0),
FrameInfo(frame=<frame object at 1A08>, filename='D:/demo.py', lineno=13, function='main', code_context=['v1()\n'], index=0),
FrameInfo(frame=<frame object at F1F0>, filename='D:/demo.py', lineno=17, function='<module>', code_context=['main()\n'], index=0)
]
于是我們可以知道傳回堆棧的傳回結果是幾個元組組成的清單。
我們的代碼調用關系是:
__main__
–>
main()
–>
f1()
–>
f2()
仔細看列印資訊,我們可以發現:
清單中的第0個元組是
f2()
的堆棧資訊
清單中的第1個元組是
f1()
的堆棧資訊
清單中的第2個元組是
main()
的堆棧資訊
清單中的第3個元組是
__main__
的堆棧資訊
層層調用,層層遞歸,最終指向最初調用的位置,
有沒有發現它和Python報錯資訊非常相像,我們确實可以用
inspect
模拟logger日志,OK來個執行個體吧:
import inspect
import re
import time
import json
class Person:
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
def toJSON(self):
return {"name": self.name, "age": self.age, "city": self.city}
def wdp(_variable):
t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# 堆棧的最裡層是該列印函數,擷取上一層的調用故取第二層堆棧
exec_info = inspect.stack()[1]
# 目前執行的檔案名
fn = exec_info[1].rsplit("/", 1)[1]
# 所在行
_line = exec_info[2]
# 調用的方法
_function = exec_info[3]
# 執行的指令
_cmd = exec_info[4][0]
# 執行的指令中參數的名稱
pattern = re.compile(
wdp.__name__ + '\((.*?)\)$',
re.S
)
_cmd = _cmd.strip().replace('\r', '').replace('\n', '')
# 變量名
vn = re.findall(pattern, _cmd.strip())[0]
# 變量轉字元串
if hasattr(_variable, "toJSON"):
info = json.dumps(_variable.toJSON(), ensure_ascii=False)
elif hasattr(_variable, "__str__"):
info = _variable.__str__()
else:
info = str(_variable)
log_info = '[{time}] [{filename}] [{func}] [{line}] {variable_name} = {variable}'.format(
time=t,
filename=fn,
func=_function,
line=_line,
variable_name=vn,
variable=info
)
print(log_info)
if __name__ == '__main__':
p = Person("王寶強", 25, "上海")
wdp(p)
最後再說說檢查對象和擷取對象
一個Python腳本中對象無外乎以下幾種:
- 子產品
- 類
- 方法
- 變量(資訊太少,
直接忽略了)inspect
于是上面的三大類又可以細分為:
- 檢查對象
- 檢查是否為子產品(
)ismodule
- 檢查是否為類()
- 是否為類(
)isclass
- 是否為基類(
)isabstract
- 是否為類(
- 檢查是否為方法()
- 是否為方法(
)ismethod
- 是否為函數(
)isfunction
- 是否為生成器函數(
)isgeneratorfunction
- 是否為生成器(
)isgenerator
- 是否為traceback(
)istraceback
- 是否為built-in函數或方法(
)isbuiltin
- 是否為使用者自定義或者built-in方法(
)isroutine
- 是否為方法辨別(
)ismethoddescriptor
- 是否為方法(
- 檢查是否為變量()
- 是否為數字辨別符
- 檢查是否為子產品(
- 擷取對象(
)getxxx
- 擷取子產品()
- 擷取子產品(
)getmodule
- 擷取子產品名(
)getmodulename
- 擷取子產品(
- 擷取類( )
- 擷取類的繼承結構(
)getmro
- 擷取方法聲明的參數(
)getargspec
- 擷取類的繼承結構(
- 擷取方法()
- 擷取方法聲明的參數(
)getargspec
- 擷取方法聲明的值(
)getargvalues
- 擷取方法聲明的參數(
- 擷取子產品()
- 子產品/類/方法都有的:
- 擷取成員(
)getmembers
- 擷取源碼(
)getsource
- 擷取源碼所在行(
)getsourcelines
- 擷取對象定義所在的子產品的檔案名 (
)getfile
- 擷取對象注釋(
)getcomments
- 擷取 對象documentation資訊(
)getdoc
- 擷取成員(
對象檢查及對象擷取看了好久,也構思了好久,但是實在是想不出有有什麼實際的用途,這裡隻列個總結,就不寫demo