3

全部,

我有一个类似于问题 2617120 的问题,可在此处找到:

  • 如何使用traceit报告函数输入变量

    当通过跟踪钩子执行 python 打印输出函数参数时,提问者需要指针。

    我正在寻找与此非常相似但有所不同的东西。我不想在代码运行时对代码进行评估,并打印出所有评估过的变量,而不是所有数据。例如,使用以下代码:

    for modname in modnames:                   
    
    if not modname or '.' in modname:      
         continue                                                                    
    ...                                  
    

    跟踪挂钩将导致打印出以下内容:

    for modname in modnames:                | for init in init,., encoding
                                            |
    if not modname or '.' in modname:       | if not init or '.' in init
         continue                           |     continue
    if not modname or '.' in modname:       | if not . or '.' in .
    ...                                     |
    

    其中代码行根据运行帧进行插值。我已经在 perl 中做到了这一点,在某些情况下它是救命稻草。

    有人对在 python 中执行此操作的最佳方式有想法吗?我有我的想法,但我想听听人们的想法(以及他们是否有任何预先制定的解决方案)

    这里,顺便说一句是参考代码:

    import sys
    import linecache
    import random
    
    def traceit(frame, event, arg):
        if event == "line":
            lineno = frame.f_lineno
            filename = frame.f_globals["__file__"]
            if filename == "<stdin>":
                filename = "traceit.py"
            if (filename.endswith(".pyc") or
                filename.endswith(".pyo")):
                filename = filename[:-1]
            name = frame.f_globals["__name__"]
            line = linecache.getline(filename, lineno)
            print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name,line.rstrip())
        return traceit
    
    
    def main():
        print "In main"
        for i in range(5):
            print i, random.randrange(0, 10)
        print "Done."
    
    sys.settrace(traceit)
    main()
    
  • 4

    1 回答 1

    0

    这是一个快速的技巧,可能会给你一些开始,给定一行文本line和当前的堆栈框架frame(顺便说一下,这是一个内部函数traceit)。

    import re
    from types import *
    
    def interpolatevar(matchobj):
        excludetypes = set((NoneType, TypeType, FunctionType, LambdaType, ClassType,
                        CodeType, InstanceType, MethodType, BuiltinFunctionType,
                        BuiltinMethodType))
    
        var = matchobj.group(0)
        basevar = var.split(".")[0]
        if basevar in frame.f_code.co_names or basevar in frame.f_code.co_varnames:
            if basevar in frame.f_globals or basevar in frame.f_locals:
                val = eval(var, frame.f_globals, frame.f_locals)
                if type(val) not in excludetypes:
                    return repr(val)
        return var
    
    line = re.sub(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*", 
                  interpolatevar, line)
    

    由于这使用正则表达式来查找标识符,即使它们在字符串文字中,它也会愚蠢地找到它们,但标识符必须在函数中实际使用并在本地或全局范围内定义。

    它确实处理对象属性访问(例如foo.bar将被该值替换),而我最初发布的版本不会,并且它过滤掉各种类型的值,因为替换foo<function foo at 0x02793470>不能真正告诉你很多你不知道的事情。(排除列表很容易定制,当然,如果您对这些类型的某些值感兴趣,或者您可以从types模块中添加其他值。)

    从长远来看,我认为查看该行的字节码可能是有利可图的,因为这样更容易识别哪些令牌实际上是标识符。该ast模块对于生成语句的解析树也可能很有用,它可以让您弄清楚标识符是什么,但是当您一次只看到一行时,这对于条件和循环来说是有问题的。

    于 2010-12-08T15:00:04.530 回答