代码之家  ›  专栏  ›  技术社区  ›  Dana Robinson

使用sys.settrace可以做什么很酷的黑客?

  •  21
  • Dana Robinson  · 技术社区  · 7 年前

    我喜欢修改发送到函数的参数,使用 settrace ,像:

    import sys
    
    def trace_func(frame,event,arg):
        value = frame.f_locals["a"]
        if value % 2 == 0:
            value += 1
            frame.f_locals["a"] = value
    
    def f(a):
        print a
    
    if __name__ == "__main__":
        sys.settrace(trace_func)
        for i in range(0,5):
            f(i)
    

    这将打印:

    1
    1
    3
    3
    5
    

    你还能用什么其他很酷的东西 设置 ?

    6 回复  |  直到 12 年前
        1
  •  24
  •   Ned Batchelder    16 年前

    我强烈建议不要滥用settrace。我假设你理解这些东西,但其他人稍后可能不会理解。有几个原因:

    1. settrace是一种非常钝的工具。OP的例子很简单,但实际上没有办法将其扩展到实际系统中。

    2. 这很神秘。任何一个来看你的代码的人都会完全困惑为什么它要做它正在做的事情。

    3. 它很慢。为执行的每一行python调用一个python函数将使程序慢许多倍。

    4. 这通常是不必要的。这里的原始示例可以通过其他几种方式完成(修改函数、将函数包装在装饰器中、通过其他函数调用它等),其中任何一种方法都比settrace更好。

    5. 很难纠正。在最初的示例中,如果您没有直接调用f,而是调用了g(它调用了f),那么跟踪函数就不会完成它的工作,因为您没有从跟踪函数返回任何值,所以它只被调用一次,然后就被忘记了。

    6. 它将阻止其他工具工作。这个程序将不可调试(因为调试程序使用setrace),它将不可跟踪,它将不可能测量它的代码覆盖率等。部分原因是缺乏对python实现者的预见性:他们给了我们setrace,但没有gettrace,所以很难有两个跟踪函数一起工作。

    跟踪函数是很酷的黑客行为。能滥用它很有趣,但请不要把它用于真正的东西。如果我听起来有点威吓,我道歉,但这是用真正的代码做的,这是一种痛苦。例如, DecoratorTools 使用trace函数执行使此语法在python 2.3中正常工作的神奇功能:

    # Method decorator example
    from peak.util.decorators import decorate
    
    class Demo1(object):
        decorate(classmethod)   # equivalent to @classmethod
        def example(cls):
            print "hello from", cls
    

    一个很好的黑客,但不幸的是,它意味着任何使用decoratortool的代码都不能与coverage.py(或者debugger)一起工作。如果你问我,这不是一个好的折衷办法。我更改了coverage.py以提供一种模式,让它与decoratortools一起工作,但我希望我没有必要这样做。

    即使标准库中的代码有时也会出错。pyexpat决定与其他扩展模块不同,并像调用Python代码一样调用跟踪函数。太糟糕了 did a bad job of it .

    </rand & gt;

        2
  •  18
  •   Gerald Kaszuba    13 年前

    我做了一个叫做 pycallgraph 它使用 sys.settrace() .

        3
  •  10
  •   Ned Batchelder    16 年前

    当然,代码覆盖是通过跟踪函数完成的。一件很酷的事情我们以前没有过,那就是分支覆盖率测量,它很好地发展起来,即将发布在alpha版本的 coverage.py .

    例如,考虑这个函数:

    def foo(x):
        if x:
            y = 10
        return y
    

    如果使用此调用测试它:

    assert foo(1) == 10
    

    然后语句覆盖率将告诉您函数的所有行都已执行。但是,当然,这个函数中有一个简单的问题:用0调用它会引发UnboundLocalError。

    分支度量会告诉您代码中有一个分支没有被完全执行,因为分支中只有一条腿被执行过。

        4
  •  3
  •   Fabian Pedregosa    14 年前

    例如,逐行获取python代码的内存消耗: http://pypi.python.org/pypi/memory_profiler

        5
  •  1
  •   Chris Redford    9 年前

    我没有一个全面的答案,但在另一个用户的帮助下,我用它做的一件事是 create a program that generates the trace tables of other Python programs .

        6
  •  1
  •   jagguli    12 年前

    python调试器pdb使用sys.settrace分析要调试的行。

    这里是PDB的C优化/扩展,它还使用sys.settrace

    https://bitbucket.org/jagguli/cpdb