python基础:inspect模块各函数的用法

目录

前言

一、inspect模块总览

1、获取成员与判断

2、获取源代码

3、类与函数

4、调用栈

二、inspect模块方法的使用

1、getmembers

2、getcomments、getdocs

3、signature 获取方法和函数签名

4、类层次体系 getmro()

5、栈与帧 currentframe()、stack()


前言

inspect模块也被称为 检查现场对象。这里的重点在于“现场”二字,也就是当前运行的状态。

inspect模块提供了一些函数来了解现场对象,包括 模块、类、实例、函数和方法

inspect函数主要用于以下四个方面

  •   对是否是模块、框架、函数进行类型检查
  •   获取源码
  •   获取类或者函数的参数信息
  •   解析堆栈

本文章会先带大家大概了解一下inspect模块有哪些功能,以及有哪些功能函数,然后再结合实例,解释每个方法他们的使用场景以及使用方法。

一、inspect模块总览

1、获取成员与判断

1. inspect.getmembers(object[, predicate])

第二个参数通常可以根据需要调用如下16个方法;

返回值为object的所有成员,以(name,value)对组成的列表

  1. inspect.ismodule(object): 是否为模块

  2. inspect.isclass(object):是否为类

  3. inspect.ismethod(object):是否为方法(bound method written in python)

  4. inspect.isfunction(object):是否为函数(python function, including lambda expression)

  5. inspect.isgeneratorfunction(object):是否为python生成器函数

  6. inspect.isgenerator(object):是否为生成器

  7. inspect.istraceback(object): 是否为traceback

  8. inspect.isframe(object):是否为frame

  9. inspect.iscode(object):是否为code

  10. inspect.isbuiltin(object):是否为built-in函数或built-in方法

  11. inspect.isroutine(object):是否为用户自定义或者built-in函数或方法

  12. inspect.isabstract(object):是否为抽象基类

  13. inspect.ismethoddescriptor(object):是否为方法标识符

  14. inspect.isdatadescriptor(object):是否为数字标识符,数字标识符有__get__ 和__set__属性; 通常也有__name__和__doc__属性

  15. inspect.isgetsetdescriptor(object):是否为getset descriptor

  16. inspect.ismemberdescriptor(object):是否为member descriptor

2. inspect.getmoduleinfo(path): 返回一个命名元组<named tuple>(name, suffix, mode, module_type)

  name:模块名(不包括其所在的package)

      suffix:

      mode:open()方法的模式,如:'r', 'a'等

      module_type: 整数,代表了模块的类型

3. inspect.getmodulename(path):根据path返回模块名(不包括其所在的package)

2、获取源代码

1. inspect.getdoc(object): 获取object的documentation信息

2. inspect.getcomments(object)

3. inspect.getfile(object): 返回对象的文件名

4. inspect.getmodule(object):返回object所属的模块名

5. inspect.getsourcefile(object): 返回object的python源文件名;object不能使built-in的module, class, mothod

6. inspect.getsourcelines(object):返回object的python源文件代码的内容,行号+代码行

7. inspect.getsource(object):以string形式返回object的源代码

3、类与函数

1. inspect.getclasstree(classes[, unique])

2. inspect.getargspec(func)

3. inspect.getargvalues(frame)

4. inspect.formatargspec(args[, varargs, varkw, defaults, formatarg, formatvarargs, formatvarkw, formatvalue, join])

5. inspect.formatargvalues(args[, varargs, varkw, locals, formatarg, formatvarargs, formatvarkw, formatvalue, join])

6. inspect.getmro(cls): 元组形式返回cls类的基类(包括cls类),以method resolution顺序;通常cls类为元素的第一个元素

7. inspect.getcallargs(func[, *args][, **kwds]):将args和kwds参数到绑定到为func的参数名;对bound方法,也绑定第一个参数(通常为self)到相应的实例;返回字典,对应参数名及其值;

4、调用栈

1. inspect.getframeinfo(frame[, context])

2. inspect.getouterframes(frame[, context])

3. inspect.getinnerframes(traceback[, context])

4. inspect.currentframe()

5. inspect.stack([context])

6. inspect.trace([context])

二、inspect模块方法的使用

1、getmembers

inspect.getmembers()函数用于发现对象的成员属性。返回的成员类型取决于我们所传入的对象类型。传入的参数可以是 模块、类、实例、函数、方法

1.1 语法

inspect.getmembers(object[, predicate])

object可以是 模块、类、实例、函数、方法;

predict表示的是谓语,可以是isclass(),ismodule()等等,用于筛选,看了后面例子就懂了。

1.2 使用场景

用于获取对象的成员属性

1.3 例子

1.3.1 传入参数为module

现在准备了两个py文件,example_inspect.py文件中存放了一些类和方法的定义,作为模块;do_inspect.py文件会将上个py文件当作模块导入;来看这个模块里面有哪些成员。如下

######################### example_inspect.py ##########################
def module_level_function(arg1, arg2, *args, **kwargs):
    """This function is declare in the module"""
    local_variable = arg1 * 2
    return local_variable


class A(object):
    """The A class"""

    def __init__(self, name):
        self.name = name

    def get_name(self):
        """Return the name of instance"""
        return self.name

    @classmethod
    def test(cls):
        pass

    @staticmethod
    def do_nothing():
        pass

instance_of_a = A("simple_instance")


class B(A):
    """This is B class
    it is Derived from A"""

    # This method is not part of A
    def do_something(self):
        """Does some works"""

    def get_name(self):
        """Overrides version from A"""
        return "B(" + self.name + ")"


######################### do_inspect.py ##########################
import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect):
    if name.startswith("__"):
        continue
    print("{} : {!r}".format(name, data))

输出:

# 输出
A : <class 'example_inspect.A'>
B : <class 'example_inspect.B'>
instance_of_a : <example_inspect.A object at 0x00000132B5F707F0>
module_level_function : <function module_level_function at 0x00000132B5F54598>

上面的代码有3个注意点:

1、我们将example_inspect.py作为模块传入getmembers()方法,可以打印出这个模块中所有的成员,并以元组列表形式放回。

2、注意到我在遍历 getmembers()方法时,使用了 if name.startwith("--"),这是为什么呢?因为模块有一些私有属性作为导入实现的一部分,还会包含一组__builtins__。这两个东西的列表非常长,我们一般也都不关心。所以使用if将他们过滤掉。

3、我们还可以将do_inspect.py写法改变一下,如下,利用getmembers的谓语参数,可以选择得到自己想要的成员变量

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect, inspect.isclass):  # 修改了这里
    if name.startswith("__"):
        continue
    print("{} : {!r}".format(name, data))

#输出
# A : <class 'example_inspect.A'>
# B : <class 'example_inspect.B'>

再修改

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect, inspect.isfunction):
    if name.startswith("__"):
        continue
    print("{} : {!r}".format(name, data))

#输出
# module_level_function : <function module_level_function at 0x000002A084C44598>

但是,当我们使用 is.method()时,会不会打印出class A、class B内的方法呢?看下面

所以当传入的参数为module时,不能打印出 类的方法。但是可以打印出 模块级别的函数。(与后面参数为 类 时做对比)

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect, inspect.ismethod):
    if name.startswith("__"):
        continue
    print("{} : {!r}".format(name, data))

# 输出为空

1.3.2 传入参数为 类

example_inspect.py不变,修改do_inspect.py文件,如下

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect.A):
    print("{} : {!r}".format(name, data))



# 输出的一部分
"""
__reduce_ex__ : <method '__reduce_ex__' of 'object' objects>
__repr__ : <slot wrapper '__repr__' of 'object' objects>
__setattr__ : <slot wrapper '__setattr__' of 'object' objects>
__sizeof__ : <method '__sizeof__' of 'object' objects>
__str__ : <slot wrapper '__str__' of 'object' objects>
__subclasshook__ : <built-in method __subclasshook__ of type object at 0x0000029958B45D38>
__weakref__ : <attribute '__weakref__' of 'A' objects>
do_nothing : <function A.do_nothing at 0x0000018ED33E1950>
get_name : <function A.get_name at 0x0000018ED33E1840>
test : <bound method A.test of <class 'example_inspect.A'>>
"""

会打印出一堆结果,这里我只粘贴上了一部分,以双下划线开始的变量基本都是继承object得来的和一些自带的变量。但是注意到,打印出了 方法:get_name()和__init__() do_nothing() test()如果我们给类A添加了类变量,同样也会打印出来

当然我们可以使用 谓语 来筛选。

注意: 要查找一个类的成员,使用 iinspect.isfunction();而inspect.ismethod()谓语只识别实例的绑定的方法;

当然,如果类里面定义了类方法,ismethod会打印出这个类方法,因为类也是对象,而类方法就是与类绑定的方法

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect.A, inspect.isfunction):
    print("{} : {!r}".format(name, data))

# 输出
"""
__init__ : <function A.__init__ at 0x0000029E5CD517B8>
do_nothing : <function A.do_nothing at 0x0000029E5CD51950>
get_name : <function A.get_name at 0x0000029E5CD51840>
"""


#########################################################################

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect.A, inspect.ismethod):
    print("{} : {!r}".format(name, data))

# 输出
"""
test : <bound method A.test of <class 'example_inspect.A'>>
"""

继承下的情况

在B继承于A情况下,B拥有了A的方法,因此也被打印出来。

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect.B, inspect.isfunction):
    print("{} : {!r}".format(name, data))


# 输出
"""
__init__ : <function A.__init__ at 0x0000020DA3E217B8>
do_nothing : <function A.do_nothing at 0x0000020DA3E21950>
do_something : <function B.do_something at 0x0000020DA3E219D8>
get_name : <function B.get_name at 0x0000020DA3E21A60>
"""

1.3.2 传入参数为 实例

可以看出,类方法和实例方法都被打印出来,而静态方法被忽略掉

import inspect
import example_inspect

for name, data in inspect.getmembers(example_inspect.instance_of_a, inspect.ismethod):
    print("{} : {!r}".format(name, data))

# 输出
"""
__init__ : <bound method A.__init__ of <example_inspect.A object at 0x00000202E9387208>>
get_name : <bound method A.get_name of <example_inspect.A object at 0x00000202E9387208>>
test : <bound method A.test of <class 'example_inspect.A'>>
"""

 

2、getcomments、getdocs

这两个函数在我们编程中用到的很少。这里也就不做介绍了。后面用到的话,直接去官方文档看就可以了。

 

3、获取源代码:getsource() 和 getsourcelines()

如果可以得到一个模块的 .py文件,则可以使用getsource() 和 getsourcelines()获取类或者方法的原始代码。

多的不说,看代码。

import inspect
import example_inspect

print(inspect.getsource(example_inspect.A))

# 输出
"""
class A(object):
    """The A class"""

    def __init__(self, name):
        self.name = name

    def get_name(self):
        """Return the name of instance"""
        return self.name

    @classmethod
    def test(cls):
        pass

    @staticmethod
    def do_nothing():
        pass
"""

####################################################

import inspect
import example_inspect

print(inspect.getsource(example_inspect.A.get_name))

# 输出
"""
    def get_name(self):
        """Return the name of instance"""
        return self.name

"""

getsourcelines()的返回值是一个tuple,其中包含一个字符串列表,和文件中源代码出现的起始行号,如下

import inspect
import example_inspect

print(inspect.getsourcelines(example_inspect.A.get_name))


# 输出
"""
(['    def get_name(self):\n', '        """Return the name of instance"""\n', '        return self.name\n'], 13)
"""

3、signature 获取方法和函数签名

3.1 (补充知识)函数签名是什么?

函数签名对象,表示调用函数的方式,即定义了函数的输入和输出

一个函数由这么bai几部分组成,函数名、参数个数、参数类型、返回值。函数签名就是指 参数个数、参数类型

那么为什么会有参数签名这么个东西呢?

答:函数在重载时,利用函数签名的不同(即参数个数与类型的不同)来区别调用者到底调用的是那个方法。就如同下面两个方法,语言正是通过他们的函数签名不同,来区别同函数名但是不同参数的函数。

def test(a,b,c):
    pass

def test(a,b,c,d):
    pass

3.2(补充知识)python函数的参数分类

共有5类,分别是

  • POSITIONAL_OR_KEYWORD:   # 普通参数, python函数的参数多数属于此类
  • VAR_POSITIONAL:*args,定位参数元组
  • VAR_KEYWORD:**kwargs,关键字参数字典
  • KEYWORD_ONLY: 仅限关键字参数, 类似于 下边d=100 的这种
  • POSITIONAL_ONLY(不用管,很难用到):仅限定位参数, python声名的函数不支持, 但是有些c语言实现且不接受关键字参数的函数, 例如: divmod 支持
# 5大类
ParameterKind = [
    {"POSITIONAL_OR_KEYWORD": "可以通过定位参数和关键字参数传入的形参, python函数的参数多数属于此类"},  # 普通参数
    {"VAR_POSITIONAL": "定位参数元组"},  # *args
    {"VAR_KEYWORD": "关键字参数字典"},  # **kwargs
    {"KEYWORD_ONLY": "仅限关键字参数"},  # 类似于 下边d=100 的这种
    {"POSITIONAL_ONLY": "仅限定位参数, python声名的函数不支持, 但是有些c语言实现且不接受关键字参数的函数, 例如: divmod 支持"},
]
 
 
 
# 前四类
from inspect import signature
 
def func(a, b, c=10, *args, d=100, **kwargs):
    print(a, b, c, args, d, kwargs)
 
sig_func = signature(func)
 
for name_, para_ in sig_func.parameters.items():
        print(para_.name, para_.kind, para_.default)
 
"""
   参数        参数类型                   参数默认值
    a       POSITIONAL_OR_KEYWORD     <class 'inspect._empty'>
    b       POSITIONAL_OR_KEYWORD     <class 'inspect._empty'>
    c       POSITIONAL_OR_KEYWORD     10
    args    VAR_POSITIONAL            <class 'inspect._empty'>
    d       KEYWORD_ONLY              100
    kwargs  VAR_KEYWORD               <class 'inspect._empty'>
"""
 
# 特殊的POSITIONAL_ONLY
 
sig_divmod = signature(divmod)
 
for _, _para in sig_divmod.parameters.items():          # 
    print(_para.name, _para.kind, _para.default)
"""
    x     POSITIONAL_ONLY     <class 'inspect._empty'>
    y     POSITIONAL_ONLY     <class 'inspect._empty'>
"""

3.2 使用inspect.signature获取函数的签名

使用inspect.signature获取函数的签名后,所有参数都以key-value的形式存放在Parameter中。我们继续是使用inspect_example.py的代码作为被检测对象。看下面的代码。

import inspect
import example_inspect

sig = inspect.signature(example_inspect.module_level_function)
print(sig.parameters)

print("\nParameter details:")
for name, param in sig.parameters.items():
    if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
        print("    {} (POSITIONAL_OR_KEYWORD)".format(name))
    elif param.kind == inspect.Parameter.VAR_POSITIONAL:
        print("    {} (VAR_POSITIONAL)".format(name))
    elif param.kind == inspect.Parameter.VAR_KEYWORD:
        print("    {} (VAR_KEYWORD)".format(name))
    elif param.kind == inspect.Parameter.KEYWORD_ONLY:
        print("    {} (KEYWORD_ONLY)".format(name))

# 输出
"""
OrderedDict([('arg1', <Parameter "arg1">), ('arg2', <Parameter "arg2">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])

Parameter details:
    arg1 (POSITIONAL_OR_KEYWORD)
    arg2 (POSITIONAL_OR_KEYWORD)
    args (VAR_POSITIONAL)
    kwargs (VAR_KEYWORD)
"""

3.4使用inspect.signature创建签名

from inspect import Signature, Parameter

# 创建一个函数参数列表,列表内的元素由类Parameter的实例组成
# Parameter实例化时,依次接受参数名、参数类型、默认值和参数注解
# 默认值和参数类型默认为空,这里的空值不是None,而是Parameter.empty,代表没有值
params = [Parameter('x', Parameter.POSITIONAL_OR_KEYWORD),
         Parameter('y', Parameter.POSITIONAL_OR_KEYWORD, default=9),
         Parameter('z', Parameter.VAR_KEYWORD)]

# 使用Signature类,接受函数参数列表,实例化出函数签名实例
sig = Signature(params)
print(sig)

这种方式获取的 sig 和我们之前使用  sig = inspect.signature(example_inspect.module_level_function) 语句获取的sig是同一类对象,本质上没有任何区别。

那么,我们把这些参数给扣出来又有什么用呢?

我们可以先为这些参数赋值,然后再去调用我们的函数。就实现了:先给参数赋值,然后再去和函数发生关联、调用。

见3.5

3.5 使用bind() 、bind_partial() 方法给匹配签名

使用函数签名的bind的方法,检查函数参数是否匹配签名。

继续延续 inspect_example.py的例子,通过函数签名的bind方法,接受函数参数。如果匹配,返回参数BoundArguments实例,如果不匹配,则抛出TypeError,并给出详细的异常信息。

通过BoundArguments实例的属性,可以获取函数签名、参数的值等内容。

 

并且,bind()函数必须指定 必传参数,默认参数不会计入到 BoundArguments中

bind_partial()函数也必须指定必传参数,默认参数不会计入到 BoundArguments中;但是如果使用了apply_defaults()方法,则默认参数也会存入arguments中。

可以认为  没有执行partial.apply_defaults()的bind_partial()函数,和bind()函数没有什么区别

也许你对上面三句话不太懂,那么好好看下面的代码例子,你就懂了。

 

bind()方法

################### inspect_example.py ###############################
def module_level_function(arg1, arg2="xxx", *args, **kwargs):
    """This function is declare in the module"""
    local_variable = arg1 * 2
    return local_variable


################### do_inspect.py ##############################
import inspect
import example_inspect

sig = inspect.signature(example_inspect.module_level_function)


bound = sig.bind(
    "this is arg1",
    "this is arg2"
)
print(bound)
example_inspect.module_level_function(*bound.args, **bound.kwargs)

#输出,可以看到BoundArguments中只有arg1这个参数,而即使arg2有默认参数,也没有将arg2传入BoundArguments
<BoundArguments (arg1='this is arg1')>
this is arg1this is arg1
xxx

 

 bind_partial()方法

################### inspect_example.py ###############################
def module_level_function(arg1, arg2="xxx", *args, **kwargs):
    """This function is declare in the module"""
    local_variable = arg1 * 2
    return local_variable


################### do_inspect.py ##############################

import inspect
import example_inspect

sig = inspect.signature(example_inspect.module_level_function)


bound = sig.bind_partial(
    "this is arg1",
    "this is arg2"
)
print(bound)
example_inspect.module_level_function(*bound.args, **bound.kwargs)

# 输出,可以看出此时bind_partial 和bind 没啥区别
"""
<BoundArguments (arg1='this is arg1')>
this is arg1this is arg1
xxx
"""

 

执行了partial.apply_defaults()方法的bind_partial()方法

################### inspect_example.py ###############################
def module_level_function(arg1, arg2="xxx", *args, **kwargs):
    """This function is declare in the module"""
    local_variable = arg1 * 2
    return local_variable


################### do_inspect.py ##############################
import inspect
import example_inspect

sig = inspect.signature(example_inspect.module_level_function)


bound = sig.bind_partial(
    "this is arg1"
)
bound.apply_defaults()  # 加了这句
print(bound)
example_inspect.module_level_function(*bound.args, **bound.kwargs)

# 输出,现在所有的默认参数都传入了BoundArguments对象
"""
<BoundArguments (arg1='this is arg1', arg2='xxx', args=(), kwargs={})>
this is arg1this is arg1
xxx
"""

4、类层次体系 getmro()

这里只介绍getmro()函数,它接受的参数为 类,然后返回值是一个 类的tuple,它会解析出传入类的所有基类,并按照mro的顺序排列。(不从mro的,自行百度吧,比较简单的概念就不说了)

看下面的例子

################### inspect_example.py ###############################
class A(object):
    """The A class"""

    def __init__(self, name):
        self.name = name

    def get_name(self):
        """Return the name of instance"""
        return self.name

    @classmethod
    def test(cls):
        pass

    @staticmethod
    def do_nothing():
        pass



class B(A):
    """This is B class
    it is Derived from A"""

    # This method is not part of A
    def do_something(self):
        """Does some works"""


    def get_name(self):
        """Overrides version from A"""
        return "B(" + self.name + ")"

################### mro_example.py ###############################
import inspect
import example_inspect


class C:
    pass


class C_First(C, example_inspect.B):
    pass


print(inspect.getmro(C_First))

#执行后,输出
"""
(<class '__main__.C_First'>, <class '__main__.C'>, <class 'example_inspect.B'>, <class 'example_inspect.A'>, <class 'object'>)
"""

5、栈与帧 currentframe()、stack()

5.1 currentframe()

为了讲清楚这里,需要补充一些知识:

5.1.1、栈帧是什么,以及程序调用与栈帧

参考这篇链接https://www.cnblogs.com/samo/articles/3092895.html

5.1.2、frame对象

看完了关于栈帧的文章,相信大家已经知道栈帧是什么,以及函数调用时是如何将信息存入栈帧的。

我们经常说的 某一帧 指的就是一个函数存储在栈中的所有信息 就是一帧, 千万不要以为某一帧就是说的某一条数据,帧是一个块的概念。 

那么frame对象又是什么呢?

Frame对象表示执行帧,表示程序运行时函数调用栈中的某一帧。

想要获得某个函数相关的栈帧,则必须在调用这个函数且这个函数尚未返回时获取。可以使用sys模块的_getframe()函数、或inspect模块的currentframe()函数获取当前栈帧。

而frame包含了一些属性,其实这些属性对应的就是我们在栈帧里存储的数据,如下

特殊只读属性
属性说明
f_back前一个堆栈帧(朝向调用者),如果这是底部堆栈帧则为None
f_code在这个框架中执行的Code对象
f_locals用于查找局部变量的字典
f_globals用于全局变量
f_builtins用于内置名称
f_restricted表示该函数是否在限制执行模式下执行的标志
f_lasti给出精确的指令(这是代码对象的字节码字符串的索引)
特殊可写属性
f_trace 
f_exc_type 
f_exc_value 
f_exc_traceback 
f_lineno当前代码在文件中的哪一行

上面这些参数,大家可以通过ID E调试,看到他们的组成。还有很多参数我没有介绍。我这里只给出一个例子,大家可以自己去调试。

import inspect
import pprint

def recurse(limit, keyword="default", * ,kwonly="must be named"):
    local_variable= "."*limit
    keyword = "change value of argument"
    frame = inspect.currentframe()
    print("line {} of {}".format(frame.f_lineno,frame.f_code.co_filename))
    pprint.pprint(frame.f_locals)

    print("\n")
    if limit <= 0:
        return
    recurse(limit-1)
    return local_variable

if __name__ == '__main__':
    recurse(2)

#输出
"""
line 8 of C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py
{'frame': <frame at 0x0000018E47160828, file 'C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py', line 9, code recurse>,
 'keyword': 'change value of argument',
 'kwonly': 'must be named',
 'limit': 2,
 'local_variable': '..'}


line 8 of C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py
{'frame': <frame at 0x0000018E46AC14D8, file 'C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py', line 9, code recurse>,
 'keyword': 'change value of argument',
 'kwonly': 'must be named',
 'limit': 1,
 'local_variable': '.'}


line 8 of C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py
{'frame': <frame at 0x0000018E46AC5668, file 'C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py', line 9, code recurse>,
 'keyword': 'change value of argument',
 'kwonly': 'must be named',
 'limit': 0,
 'local_variable': ''}
"""

5.2 stack()

使用stack()函数,还可以访问到当前帧到第一个调用者的所有栈帧。这个例子与上面的例子相似。只不过它会一直等待,直到递归结束,再打印栈信息。

其实stack()获取的就是frame的列表,是一个按照调用顺序包含了所有栈帧的列表。

那么,它有什么用处呢?

我用它排插过错误,通过定位栈,来定位错误的来源。其他的场景还没有用过。如果后面有的话,再补充。

import inspect
import pprint


def show_stack():
    for level in inspect.stack():
        print("{}[{}]\n -> {}".format(level.frame.f_code.co_filename,
                                      level.lineno,
                                      level.code_context[level.index].strip()))
        pprint.pprint(level.frame.f_locals)
        print("\n")


def recurse(limit):
    local_variable = "." * limit
    if limit <= 0:
        show_stack()
        return
    recurse(limit - 1)
    return local_variable


if __name__ == '__main__':
    recurse(2)


#输出
"""
C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py[6]
 -> for level in inspect.stack():
{'level': FrameInfo(frame=<frame at 0x000001A6D0FF8798, file 'C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py', line 9, code show_stack>, filename='C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py', lineno=6, function='show_stack', code_context=['    for level in inspect.stack():\n'], index=0)}


C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py[16]
 -> show_stack()
{'limit': 0, 'local_variable': ''}


C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py[18]
 -> recurse(limit - 1)
{'limit': 1, 'local_variable': '.'}


C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py[18]
 -> recurse(limit - 1)
{'limit': 2, 'local_variable': '..'}


C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py[23]
 -> recurse(2)
{'__annotations__': {},
 '__builtins__': <module 'builtins' (built-in)>,
 '__cached__': None,
 '__doc__': None,
 '__file__': 'C:/Users/ASUS/Desktop/项目资料/模型引擎项目/luigi_process_test/code/standard_test/do_inspect.py',
 '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A6CFA71CC0>,
 '__name__': '__main__',
 '__package__': None,
 '__spec__': None,
 'inspect': <module 'inspect' from 'C:\\Users\\ASUS\\Anaconda3\\lib\\inspect.py'>,
 'pprint': <module 'pprint' from 'C:\\Users\\ASUS\\Anaconda3\\lib\\pprint.py'>,
 'recurse': <function recurse at 0x000001A6D0444598>,
 'show_stack': <function show_stack at 0x000001A6CFAAC1E0>}

"""

 

 

  • 36
    点赞
  • 150
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值