Vâng, đây là loại phương thức chậm. Nó có thể được sửa đổi để xem thay đổi biến cục bộ (chỉ bằng tên). Đây là cách nó hoạt động: chúng tôi làm sys.settrace và phân tích giá trị của obj.attr mỗi bước. Phần khó khăn là chúng tôi nhận được 'line'
sự kiện (một số dòng đã được thực thi) trước khi thực hiện dòng. Vì vậy, khi chúng ta nhận thấy obj.attr đã thay đổi, chúng ta đã ở trên dòng tiếp theo và chúng ta không thể có được khung hình trước đó (vì khung không được sao chép cho mỗi dòng, chúng được sửa đổi). Vì vậy, trên mỗi sự kiện dòng tôi lưu traceback.format_stack
đến watcher.prev_st
và nếu cuộc gọi tiếp theo của trace_command
giá trị đã thay đổi, chúng tôi sẽ in dấu vết ngăn xếp đã lưu vào tệp. Tiết kiệm traceback trên mỗi dòng là một hoạt động khá tốn kém, vì vậy bạn phải đặt từ khóa include
thành danh sách thư mục dự án của bạn (hoặc chỉ là thư mục gốc của dự án) để không xem các thư viện khác đang làm gì và lãng phí CPU.
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()
Có, điều này rất chậm, nhưng vẫn nhanh hơn pdb thủ công, cảm ơn bạn. – Bunyk
vâng, đã sửa. Nhân tiện, nếu bạn đồng ý với dòng tiếp theo thay vì thực tế, nó có thể được thực hiện theo cách nhanh hơn nhiều, hãy kiểm tra điều này: https://gist.github.com/4086770 nó hiển thị dòng tiếp theo hoặc một thực tế, tùy thuộc vào sự kiện 'line' theo sự kiện' line' –