观察python中的变量变化 [英] Watch for a variable change in python

查看:178
本文介绍了观察python中的变量变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



它应该是sqlalchemy.orm.attributes.InstrumentedAttribute,但是当它的一个属性在某个地方有错误的值时,我运行测试是恒定值,我们来说说字符串。



有一种方法可以在调试模式下运行python程序,并运行一些检查(如果变量更改类型)每一步自动执行代码行?



PS我知道如何通过检查和属性装饰器的帮助记录类实例的属性的更改。可能在这里我可以使用这种方法与元类...



但有时我需要更多的一般和强大的解决方案...



谢谢。



PPS我需要这样的东西: http://stackoverflow.com/a/7669165/816449 ,但可能会有更多的解释该代码正在发生什么。

解决方案

嗯,这是一种慢的方法。它可以修改为观看局部变量更改(只是按名称)。这是它的工作原理:我们做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)

监视对象属性更改的调试器
obj - 要查看的对象
attr - 字符串,属性名称
log_file - 字符串,写入输出的位置
include - 字符串列表,仅在这些目录中调试文件
将其设置为项目路径,否则将需要很长时间时间
运行在大图书馆导入和使用。


s elf.log_file = log_file
with open(self.log_file,'wb'):pass
self.prev_st = None
self.include = [incl.replace('\\' ,'/')包含]
如果obj:
self.value = getattr(obj,attr)
self.obj = obj
self.attr = attr
self.enabled = enabled#重要,必须是__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):
如果event!='line'或不self.enabled:
return self.trace_command
如果self.check_condition():
如果self.prev_st:
with open(self.log_file,'ab')as f:
print>> f,价值,self.obj,,self.attr,改了!
print>> f,######行:
print>> f,''。join(self.prev_st)
if self.include:
fname = frame.f_code.co_filename.replace('\\','/')
to_include = False
包含在self.include中:
如果fname。 startswith(incl):
to_include = True
break
如果不是to_include:
返回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
对于我在xrange(3)中:
打印'a'
x.foo = i

for x in xrange(1):
i = i + 1

main()


There is large python project where one attribute of one class just have wrong value in some place.

It should be sqlalchemy.orm.attributes.InstrumentedAttribute, but when I run tests it is constant value, let's say string.

There is some way to run python program in debug mode, and run some check (if variable changed type) after each step throught line of code automatically?

P.S. I know how to log changes of attribute of class instance with help of inspect and property decorator. Possibly here I can use this method with metaclasses...

But sometimes I need more general and powerfull solution...

Thank you.

P.P.S. I need something like there: http://stackoverflow.com/a/7669165/816449, but may be with more explanation of what is going on in that code.

解决方案

Well, here is a sort of slow approach. It can be modified for watching for local variable change (just by name). Here is how it works: we do sys.settrace and analyse the value of obj.attr each step. The tricky part is that we receive 'line' events (that some line was executed) before line is executed. So, when we notice that obj.attr has changed, we are already on the next line and we can't get the previous line frame (because frames aren't copied for each line, they are modified ). So on each line event I save traceback.format_stack to watcher.prev_st and if on the next call of trace_command value has changed, we print the saved stack trace to file. Saving traceback on each line is quite an expensive operation, so you'd have to set include keyword to a list of your projects directories (or just the root of your project) in order not to watch how other libraries are doing their stuff and waste 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()

这篇关于观察python中的变量变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆