글쎄, 여기에 일종의 느림 접근 방식이 있습니다. 로컬 변수 변경 (이름 만)을 감시하기 위해 수정할 수 있습니다. 작동 원리는 다음과 같습니다. sys.settrace를 수행하고 각 단계의 obj.attr 값을 분석합니다. 까다로운 부분은 행이 실행되기 전에 'line'
이벤트 (일부 행이 실행 되었음)를 수신한다는 것입니다. 따라서 obj.attr이 변경되었다는 것을 알게되면 이미 다음 줄에 있습니다. 이전 줄 프레임을 가져올 수 없습니다 (각 줄마다 프레임이 복사되지 않으므로 수정됩니다). 따라서 각 라인 이벤트에서 traceback.format_stack
을 watcher.prev_st
으로 저장하고 다음 호출시 trace_command
값이 변경되면 저장된 스택 추적을 파일로 인쇄합니다. 각 라인에 추적을 저장하는 것은 꽤 비싼 작업입니다. 따라서 include
키워드를 프로젝트 디렉토리 목록 (또는 프로젝트의 루트)으로 설정하여 다른 라이브러리가 자신의 물건을 어떻게 낭비하고 낭비 하는지를 보지 않아야합니다 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()
예, 이것은 매우 느리지 만 수동 pdb보다 빠릅니다. 감사합니다. – Bunyk
예, 수정 됨. 그건 그렇고, 당신이 실제가 아닌 다음 라인을 사용해도 괜찮다면, 훨씬 더 빠른 방법으로 이것을 할 수 있습니다. https://gist.github.com/4086770 다음 라인이나 실제'line' 이벤트가'line' 이벤트 다음에 오는 지 여부에 따라 달라집니다. –