我喜欢能够使用
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
做哪些其他很酷的事情?
我强烈建议不要滥用 settrace。我假设您了解这些内容,但稍后出现的其他人可能不会。有以下几个原因:
Settrace 是一个非常生硬的工具。 OP 的示例很简单,但实际上没有办法将其扩展以在实际系统中使用。
这很神秘。任何人来看你的代码都会完全困惑为什么它正在做它正在做的事情。
速度很慢。为执行的每一行 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 代码一样调用跟踪函数。太糟糕了,他们做得很糟糕。
当然,代码覆盖率是通过trace函数完成的。我们以前没有过的一件很酷的事情是分支覆盖率测量,而且进展顺利,即将在 coverage.py.
的 alpha 版本中发布。例如,考虑这个函数:
def foo(x):
if x:
y = 10
return y
如果您使用此调用进行测试:
assert foo(1) == 10
then 语句覆盖率会告诉你函数的所有行都被执行了。但当然,该函数中有一个简单的问题:用 0 调用它会引发 UnboundLocalError。
分支测量会告诉您代码中存在一个未完全执行的分支,因为只采用了该分支的一条分支。
例如逐行获取Python代码的内存消耗:http://pypi.python.org/pypi/memory_profiler
我没有一个详尽全面的答案,但我用它做的一件事是,在另一位用户的帮助下,创建一个程序来生成其他Python程序的跟踪表。
python 调试器 Pdb 使用 sys.settrace 来分析要调试的行。
这是 pdb 的 C 优化/扩展,也使用 sys.settrace
使用 settrace 的一个非常酷的项目以一种很酷的方式将 defer 语句从 go 添加到语言中。它允许这样的代码:
def foo():
print(", world!") in defer
print("Hello", end="")
# do something that might fail...
assert 1 + 1 == 3
这将输出:
$ python foo.py
Hello, World!
Traceback (most recent call last):
File "foo.py", line 7, in <module>
assert 1 + 1 == 3
AssertionError