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

2023-11-17

目录

前言

一、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>}

"""

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python基础:inspect模块各函数的用法 的相关文章

随机推荐

  • 龙湖股价快速收复失地,近4日累计上涨27.8%

    11月7日早盘 龙湖集团股价快速冲高 截至10点 报13 1港元 对应涨幅为2 5 全面收付上周一深调失地 从近期表现来看 龙湖集团股价已连续反弹多日 截至上周五收盘 4个交易日内累计上涨27 8 在过去一周内 多家地产公司的波动令市场情绪
  • 手写数字的识别分类+技术总结

    1 学习转载一篇关于机器学习手写数字的识别 Python 3 利用机器学习模型 进行手写体数字检测 Python 3 生成手写体数字数据集 2 技术总结 机器学习代码实现的初级阶段 既要自己上手项目 也要多看其他前辈的博文 特别是一些附加有
  • 测试工程师要进阶,就得明白这3点...

    在大型互联网公司带测试团队多年 也做过多年的测试工程师招聘工作 对测试工程师的能力也见过高高低低各种 今天 分享下这些年带领测试团队提高的一点思路和方法 还在成长期的测试工程师可以用来指导自己的学习 用这套框架给自己制定一套目标和学习框架
  • Word、WPS中表格的第一行和第二行中间间距,如何去掉,使得跟其他行间距相同

    Word WPS中表格的第一行和第二行中间间距 如何去掉 使得跟其他行间距相同 Word或wps表格已经设计好了很多表格样式供大家使用 如图1 图1 当不想第一行和第二行的间距相同时 取消首行填充即可 如果不间距未变 再将首行删除必行
  • 嵌入式Linux开发笔记之手动设置交叉编译工具链

    快速索引 开发平台 拷贝交叉编译工具链到Linux主机 1 通过 MobaXterm 远程登录 Linux主机 安装交叉编译工具链 1 创建 imx6ull toolchain目录存放交叉编译工具链 2 解压交叉编译工具 3 设置工具链永久
  • 博弈论

    文章目录 博弈论 什么是博弈论 博弈的前提 博弈的要素 博弈的分类 非合作博弈 有限两人博弈囚徒困境 合作博弈 无限多人博弈囚徒困境 常见的博弈定律 零和博弈 重复博弈 智猪博弈 斗鸡博弈 猎鹿博弈 蜈蚣博弈 酒吧博弈 枪手博弈 警匪博弈
  • react使用阿里云对象存储,ali-oss, antd upload to ali-oss

    最近写阿里云图片上传 碰到一些小问题 在此总结一下 项目环境 create react app antd node6 1 0 看了阿里云oss对象存储sdk 直接采用node 的安装方式 在使用的时候碰到了问题 yield client p
  • Partial Evaluation

    Scrapping your Inefficient Engine Using Partial Evaluation to Improve Domain Specific Language Implementation AnyDSL A P
  • C++程序基础(15):C++while循环例题

    C while循环例题 前言 例题 1 1 含k个3的数 做法分析 完整代码演示 前言 通过上节的介绍 大家对while循环语句想必也有了一定的了解 那么在本篇中我们将来看一道较为复杂的例题 帮助大家巩固和进一步理解while循环语句 例题
  • 四大含金量高的算法证书考试

    证书考试推荐 一 PAT 计算机程序设计能力测试 二 CCF CSP认证 三 团体程序设计天梯赛 四 蓝桥杯大赛 一 PAT 计算机程序设计能力测试 官网 PAT 计算机程序设计能力测试 PAT为浙江大学出的一款程序设计的测试网站 分为乙级
  • 在VS里面怎么用scanf函数

    大家请看这个页面 认为scanf函数不安全 考虑使用scanf s替换 但是这个scanf s函数是VS这个IDE提供的 不是C语言标准库中的函数 这就意味着你在你代码中使用了scanf函数 你的代码在VS编译器上是不能通过编译的 如果不用
  • 【Linux学习笔记】管理Linux操作系统:简单的关闭防火墙

    临时关闭防火墙 命令查看防火墙状态 root rst firewall cmd state running 防火墙处于开启状态 停止防火墙 root rst systemctl stop firewalld service root rst
  • 使用Java播放MP3或Wav音频

    JavaSound是一个小巧的低层应用程序接口 API 它支持数字音频和乐器数字接口 MIDI 数据的记录和回放 在JDK 1 3 0之前 JavaSound是一个标准的Java扩展API 但从Java 2的1 3 0版开始 JavaSou
  • Git Bash 中使用 Python 命令

    Git Bash 中使用 Python 命令 在 Windows 系统中 安装 Git 后可以在任何位置右键点击使用 Git Bash 在 Git Bash 中可以使用 Linux 中的命令 在 Git Bash 中可以使用 python
  • 基于GroundingDINO构建目标检测数据集自动标注工具-支持COCO和VOC格式

    0 介绍 GroundingDINO是最新发布的zero shot目标检测器 在多个数据集上取得了很好的结果 在该方法的基础上 本人修复该方法自动标注的bug 并增加VOC格式 1 安装GroundingDINO 根据github提供的源码
  • 【云原生之Docker实战】在Docker环境部署Answer问答平台

    云原生之Docker实战 在Docker环境部署Answer问答平台 一 Answer介绍 二 检查本地Docker环境 1 检查本地Docker版本 2 检查Docker状态 3 检查docker compose版本 三 下载Answer
  • Binder的工作机制浅析

    在Android开发中 Binder主要用于Service中 包括AIDL和Messenger 其中Messenger的底层实现就是AIDL 所以我们这里通过AIDL来分析一下Binder的工作机制 一 在Android Studio中建立
  • AI绘画指南 如何设置与使用 stable diffusion webui (SD webui)

    分享最近对AI绘画的理解和实践经验 希望帮助那些对AI绘画有兴趣但不知如何入门的人 分享的内容主要包括对stable diffusion webui界面的介绍 解释参数的含义和如何进行调整 此外 还会介绍如何在txt2img中设置特征点 以
  • 打开方式无法添加程序的解决办法

    打开方式无法添加程序的解决办法 右键选择打开方式 发现想要的程序没有在列表里面 点击浏览 选择想要的这个程序 点击打开 回到了选择列表 发现没有把想要的程序加进去 该如何处理 方案一 想要使用的exe程序在某个中文命名的文件夹中 这样是添加
  • python基础:inspect模块各函数的用法

    目录 前言 一 inspect模块总览 1 获取成员与判断 2 获取源代码 3 类与函数 4 调用栈 二 inspect模块方法的使用 1 getmembers 2 getcomments getdocs 3 signature 获取方法和