创建一个行为类似于任何变量但在更改/读取时具有回调的类

时间:2012-03-24 17:47:23

标签: python class callback variable-assignment descriptor

我想创建一个行为python变量的类,但在更改/读取“变量”时调用一些回调函数。

换句话说,我希望能够按如下方式使用该课程:

x=myClass(change_callback, read_callback)

将x定义为myclass的一个实例。构造函数( INIT )将2个函数作为参数:每次更改x时调用的函数,以及每次x“读取”时调用的函数

以下声明:

x=1

将“保存”1并触发对change_callback(1)的调用,该调用可以执行任何操作。

以下声明:

a=x

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

我希望这适用于任何类型,例如能够写出如下内容:

x=[1 2 3]会触发change_callback([1 2 3])

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

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

当然,

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

这个想法是,可以记录对变量的任何访问,或者无缝地生成其他计算。

我觉得这应该是可能的,但我真的没有看到我的对象应该继承的内容...... 如果我必须限制我一种类型,例如一个列表,有没有办法在“一次性”中重新定义所有赋值运算符(包括append()...)等方法,保持原始行为(基类方法)并添加回调...

还是有更合适的方式(模块......)来实现相同的目标......?

提前致谢,

4 个答案:

答案 0 :(得分:8)

对象不知道何时将它们分配给变量。写x = a添加指向 a 的词条(或本地条目)。 a 对象不会得到通知(但是,在CPython中,它的引用计数会增加)。

获得通知的部分是分配对象的容器。在全局赋值的情况下,模块字典会更新。在像a.b = x这样的实例变量更新的情况下,有一个虚线查找并存储到实例字典中。

您可以使这些容器在查找或存储上调用任意代码。 Python的 property 为新式类提供了这种功能。通过 exec eval 操作,您可以指定可以为常规分配提供此功能的自定义字典。在这两种情况下,您都可以完全控制分配时发生的事情。

摘要:查找和存储行为可通过分配的目标进行控制,而不是分配对象

示例

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()

答案 1 :(得分:4)

您可能希望查看描述符:http://docs.python.org/howto/descriptor.html

这只适用于对象属性,这对您来说已经足够了。

答案 2 :(得分:2)

你无法使用=运算符执行此操作 - 因为根据定义,它旨在覆盖左侧,无论当前内容是什么,都不会收到任何通知。 (并且赋值运算符在Python中无法覆盖。)

但是,您可以在课堂上使用.set().get()方法。它不会给你带来相同的“魔力”,但老实说,这可能是一件好事 - 它会让你更清楚发生了什么。

答案 3 :(得分:1)

Variable是对python对象的引用。说“一个类似行为变量”是错误的。 当你做的时候

x = [1, 2, 3]

变量x将成为新列表对象[1, 2, 3]的引用(重定向),而不是旧对象“已更改”。它只是失去了一个参考。

做你想做的事。尝试在班级的change_callback方法中拨打__init__

要调用read_callback。做这样的事情

class A(object):
    def __init__(self): 
        self._x = 3
    @property
    def x():
        read_callback() # called!
        return self._x
    ....
a = A()
print a.x

如果这就是你的意思。