天天看點

在實戰中學習python的inspect子產品(堆棧解析為主)

官網對inspect子產品的解釋是:

inspect子產品主要提供了四種用處:

  1. 對是否是子產品,架構,函數等進行類型檢查
  2. 擷取源碼
  3. 擷取類或函數的參數資訊
  4. 解析堆棧

說白了就是以下三大類:

  1. 檢查對象(isxxx)
  2. 擷取對象(getxxx)
  3. 解析堆棧對象執行個體

先說說解析堆棧對象執行個體,個人感覺這是最有用的

我們可以寫個Demo來模拟log日志列印

我們希望列印的時候實作以下需求:

  1. 列印變量值,也列印變量名
  2. 列印出是在哪個檔案,哪個方法,哪一行執行列印了
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子產品(堆棧解析為主)

最後再說說檢查對象和擷取對象

一個Python腳本中對象無外乎以下幾種:

  1. 子產品
  2. 方法
  3. 變量(資訊太少,

    inspect

    直接忽略了)

于是上面的三大類又可以細分為:

  1. 檢查對象
    1. 檢查是否為子產品(

      ismodule

    2. 檢查是否為類()
      1. 是否為類(

        isclass

      2. 是否為基類(

        isabstract

    3. 檢查是否為方法()
      1. 是否為方法(

        ismethod

      2. 是否為函數(

        isfunction

      3. 是否為生成器函數(

        isgeneratorfunction

      4. 是否為生成器(

        isgenerator

      5. 是否為traceback(

        istraceback

      6. 是否為built-in函數或方法(

        isbuiltin

      7. 是否為使用者自定義或者built-in方法(

        isroutine

      8. 是否為方法辨別(

        ismethoddescriptor

    4. 檢查是否為變量()
      1. 是否為數字辨別符
  2. 擷取對象(

    getxxx

    )
    1. 擷取子產品()
      1. 擷取子產品(

        getmodule

      2. 擷取子產品名(

        getmodulename

    2. 擷取類( )
      1. 擷取類的繼承結構(

        getmro

      2. 擷取方法聲明的參數(

        getargspec

    3. 擷取方法()
      1. 擷取方法聲明的參數(

        getargspec

      2. 擷取方法聲明的值(

        getargvalues

  3. 子產品/類/方法都有的:
    1. 擷取成員(

      getmembers

    2. 擷取源碼(

      getsource

    3. 擷取源碼所在行(

      getsourcelines

    4. 擷取對象定義所在的子產品的檔案名 (

      getfile

    5. 擷取對象注釋(

      getcomments

    6. 擷取 對象documentation資訊(

      getdoc

對象檢查及對象擷取看了好久,也構思了好久,但是實在是想不出有有什麼實際的用途,這裡隻列個總結,就不寫demo