使用sys.settrace可以实现哪些有趣的黑科技?
我很喜欢用 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
使用 settrace
你还可以做些什么有趣的事情呢?
8 个回答
当然,代码覆盖率是通过跟踪功能来实现的。我们之前没有的一个很酷的功能是分支覆盖率测量,这个功能正在顺利开发中,快要在coverage.py的测试版中发布了。
举个例子,考虑这个函数:
def foo(x):
if x:
y = 10
return y
如果你用这个调用来测试它:
assert foo(1) == 10
那么语句覆盖率会告诉你这个函数的所有行都被执行了。但是,这个函数里有一个简单的问题:用0来调用它会引发一个UnboundLocalError(未绑定的局部变量错误)。
分支测量会告诉你,代码中有一个分支没有被完全执行,因为这个分支的只有一条路径被走过。
我做了一个叫做 pycallgraph
的模块,它可以通过 sys.settrace()
来生成调用图。
我强烈建议不要滥用settrace这个工具。我假设你对这些内容有一定了解,但后面可能会有其他人不太明白。这里有几个原因:
settrace是个很笨重的工具。虽然提问者的例子很简单,但几乎没有办法把它扩展到真正的系统中去使用。
它很神秘。任何人看到你的代码都会完全搞不懂它为什么会这样运行。
它很慢。每执行一行Python代码就调用一次Python函数,这会让你的程序变得非常慢。
通常是没必要的。这里的原始例子可以用其他几种方法实现(比如修改函数、用装饰器包裹函数、通过另一个函数调用等),这些方法都比settrace要好。
很难正确使用。在原始例子中,如果你不是直接调用f,而是调用g再调用f,你的跟踪函数就无法正常工作,因为你从跟踪函数返回了None,所以它只会被调用一次,然后就被遗忘了。
它会让其他工具失效。这个程序将无法调试(因为调试器使用settrace),也无法进行跟踪,无法测量代码覆盖率等等。这部分是因为Python的开发者没有考虑周全:他们给了我们settrace,但没有gettrace,所以很难让两个跟踪函数一起工作。
跟踪函数可以用来做一些很酷的黑科技。能够滥用它确实很有趣,但请不要把它用在实际项目中。如果我说得有点严厉,我很抱歉,但在真实代码中这样做会很麻烦。例如,DecoratorTools使用了一个跟踪函数,让这种语法在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
这是个不错的黑科技,但不幸的是,任何使用DecoratorTools的代码都无法与coverage.py(或者调试器)一起工作。如果你问我,这样的权衡并不好。我修改了coverage.py,提供了一种模式让它可以与DecoratorTools一起工作,但我真希望我不需要这样做。
甚至标准库中的代码有时也会搞错这些事情。Pyexpat决定和其他扩展模块不同,以Python代码的方式调用跟踪函数。可惜他们做得很糟糕。
</rant>