嗯,這裏是一個排序緩慢方法。它可以被修改用於觀察局部變量變化(僅通過名稱)。以下是它的工作方式:我們執行sys.settrace並分析obj.attr每一步的值。棘手的部分是我們在執行行之前收到'line'
事件(某行已執行)。所以,當我們注意到obj.attr已經改變時,我們已經在下一行,並且我們不能得到以前的行框架(因爲沒有爲每一行復制框架,它們被修改)。因此,在每一行事件中,我將traceback.format_stack
保存爲watcher.prev_st
,如果在下次調用trace_command
時值已更改,我們將保存的堆棧跟蹤打印到文件。在每一行上保存回溯操作都是相當昂貴的操作,因此您必須將include
關鍵字設置爲您的項目目錄列表(或者僅僅是項目的根目錄),以免看到其他庫如何處理它們的內容和浪費中央處理器。
watcher.py
import traceback
class Watcher(object):
def __init__(self, obj=None, attr=None, log_file='log.txt', include=[], enabled=False):
"""
Debugger that watches for changes in object attributes
obj - object to be watched
attr - string, name of attribute
log_file - string, where to write output
include - list of strings, debug files only in these directories.
Set it to path of your project otherwise it will take long time
to run on big libraries import and usage.
"""
self.log_file=log_file
with open(self.log_file, 'wb'): pass
self.prev_st = None
self.include = [incl.replace('\\','/') for incl in include]
if obj:
self.value = getattr(obj, attr)
self.obj = obj
self.attr = attr
self.enabled = enabled # Important, must be last line on __init__.
def __call__(self, *args, **kwargs):
kwargs['enabled'] = True
self.__init__(*args, **kwargs)
def check_condition(self):
tmp = getattr(self.obj, self.attr)
result = tmp != self.value
self.value = tmp
return result
def trace_command(self, frame, event, arg):
if event!='line' or not self.enabled:
return self.trace_command
if self.check_condition():
if self.prev_st:
with open(self.log_file, 'ab') as f:
print >>f, "Value of",self.obj,".",self.attr,"changed!"
print >>f,"###### Line:"
print >>f,''.join(self.prev_st)
if self.include:
fname = frame.f_code.co_filename.replace('\\','/')
to_include = False
for incl in self.include:
if fname.startswith(incl):
to_include = True
break
if not to_include:
return self.trace_command
self.prev_st = traceback.format_stack(frame)
return self.trace_command
import sys
watcher = Watcher()
sys.settrace(watcher.trace_command)
testwatcher.py
from watcher import watcher
import numpy as np
import urllib2
class X(object):
def __init__(self, foo):
self.foo = foo
class Y(object):
def __init__(self, x):
self.xoo = x
def boom(self):
self.xoo.foo = "xoo foo!"
def main():
x = X(50)
watcher(x, 'foo', log_file='log.txt', include =['C:/Users/j/PycharmProjects/hello'])
x.foo = 500
x.goo = 300
y = Y(x)
y.boom()
arr = np.arange(0,100,0.1)
arr = arr**2
for i in xrange(3):
print 'a'
x.foo = i
for i in xrange(1):
i = i+1
main()
是的,這是非常緩慢的,但仍然比手動pdb更快,謝謝。 – Bunyk
是的,修好了。順便說一句,如果您對下一行而不是實際行得通,可以以更快的方式完成,請查看:https://gist.github.com/4086770它顯示下一行或下一行實際的一個,取決於'line'事件是否在'line'事件之後 –