创建一个类,其行为像任何变量,但有回调on change / read [英] creating a class that behaves like any variable but has callback on change/read

查看:134
本文介绍了创建一个类,其行为像任何变量,但有回调on change / read的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个类,它作为一个python变量,但是当变量被改变/读取时调用一些回调函数。



换句话说,我想使用类如下:

  x = myClass(change_callback,read_callback)

将x定义为myclass的实例。构造函数( INIT )需要2个函数作为参数:每当x被改变时被调用的函数,以及每当x被读取时被调用的函数



以下语句:

  x = 1 

会保存1并触发对 change_callback(1)的调用, / p>

以下语句:

  a = x 

将检索存储的值并调用 read_callback()更改存储的值并执行其他操作。



我想要使用任何类型,例如能够写出如下内容:



x = [1 2 3] c> change_callback([1 2 3])



x.append(4)会触发 change_callback([1 2 3 4])(可能调用 read_callback() / p>

x = {'a':1} 会触发 change_callback({'a' :1})



print(x)会触发对read_callback )...并返回最后存储的值进行打印。



这个想法是可以记录对变量的任何访问,或生成其他计算。 。貌似没有。



我得到的感觉这应该是可能的,但我真的不知道我的对象应该继承...
如果我有限制我一种类型,例如一个列表,是否有任何方式重新定义所有的赋值运算符(包括像append()...)在一次,保持原来的行为(基类方法)和添加回调...



还是有更合适的方法(模块...)来达到相同的目标...?



解决方案

对象不知道它们何时被分配给一个变量。写 x = a 添加指向 的dict条目(或locals条目)。 对象不会得到通知(虽然,在CPython中,它的引用计数增加)。



分配对象的容器。在全局赋值的情况下,模块字典被更新。在像 a.b = x 的实例变量更新的情况下,有一个虚线查找和存储到实例字典。



您可以使这些容器在查找或存储上调用任意代码。 Python的 属性 为新式类提供此功能。 exec 更改 操作可让您指定可为常规分配提供此功能的自定义字典。



摘要:查找和存储行为可通过



 从集合import namedtuple 

CallbackVar = namedtuple('CallbackVar',['change_callback','read_callback'])

class CallbackDict(dict):
'CallbackVar'
实例回调的dict的变体def __getitem __(self,key):
value = dict。 __getitem __(self,key)
if isinstance(value,CallbackVar):
return value.read_callback(key)
def __setitem __(self,key,value):
try:
realvalue = dict .__ getitem __(self,key)
如果isinstance(realvalue,CallbackVar):
return realvalue.change_callback(key,value)
,除了KeyError:
pass
return dict .__ setitem __(self,key,value)

stmts ='''
x = CallbackVar(setter,getter)#将getter()和setter x
x = 1#调用setter()
x#调用getter()
'''

def getter(key):
print 'get',key
return 42

def setter(key,value):
print'setting',key,'to',value
$ b b exec stmts in globals(),CallbackDict()


I would like to create a class that behaves as a python variable but calls some callback function when the "variable" is changed/read.

In other words, I'd like to be able to use the class as follows:

x=myClass(change_callback, read_callback)

defines x as an instance of myclass. The constructor (INIT) takes 2 functions as paramater: a function to be called each time x is changed, and a function to be called each time x is "read"

The following statement:

x=1

would "save" 1 and trigger a call to change_callback(1) which could do anything.

The following statement:

a=x

would retrieve the stored value and call read_callback() which would possibly change the stored value and do other things.

I would like this to work with any type, e.g. to be able to write things like:

x=[1 2 3] which would trigger change_callback([1 2 3])

x.append(4) would trigger change_callback([1 2 3 4]) (and possibly a call to read_callback() before)

x={'a':1} would trigger change_callback({'a':1})

print(x) would trigger a call to read_callback()...and return the last stored value for printing, of course.

The idea is that any access to the variable could be logged, or generate other calculation... seemlessly.

I get the feeling this should be possible, but I don't really see what my object should inherit from... If I have to restrict me to one type,e.g. a list, is there any way to redefine all assignment operators (including methods like append()...) in "one go", keeping the original behaviour (the base class method) and adding the callback...

Or are there more appropriate ways (modules...) to achieve the same goals...?

Thanks in advance,

解决方案

Objects don't know when they get assigned to a variable. Writing x = a adds a dict entry (or locals entry) that points to a. The a object doesn't get notified (though, in CPython, its reference count gets incremented).

The part that does get notified is the container where the object is assigned. In the case of a global assignment, the module dictionary gets updated. In the case of instance variable updates like a.b = x, there is a dotted lookup and store to the instance dictionary.

You can make those containers invoke arbitrary code on the lookup or store. Python's property provides this capability to new-style classes. The exec and eval operations let you specify a custom dict that can provide this capability to regular assignments. In both cases, you are in complete control of what happens upon assignment.

Summary: Lookup and store behaviors are controllable through the target of the assignment rather than the object being assigned.

Example:

from collections import namedtuple

CallbackVar = namedtuple('CallbackVar', ['change_callback', 'read_callback'])

class CallbackDict(dict):
    'Variant of dict that does callbacks for instances of CallbackVar'
    def __getitem__(self, key):
        value = dict.__getitem__(self, key)
        if isinstance(value, CallbackVar):
            return value.read_callback(key)
    def __setitem__(self, key, value):
        try:
            realvalue = dict.__getitem__(self, key)
            if isinstance(realvalue, CallbackVar):
                return realvalue.change_callback(key, value)
        except KeyError:
            pass
        return dict.__setitem__(self, key, value)

stmts = '''
x = CallbackVar(setter, getter)     # Attach getter() and setter() to "x"
x = 1                               # Invoke the setter()
x                                   # Invoke the getter()
'''

def getter(key):
    print 'Getting', key
    return 42

def setter(key, value):
    print 'Setting', key, 'to', value

exec stmts in globals(), CallbackDict()

这篇关于创建一个类,其行为像任何变量,但有回调on change / read的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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