天天看点

在实战中学习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