跟踪实例中属性的更改。蟒蛇

时间:2011-07-17 23:33:10

标签: python

我想实现以任何对象作为参数的函数,并跟踪特定属性的值的变化。比在old_name属性中保存属性的旧值。

例如:

class MyObject(object):
     attr_one = None
     attr_two = 1

让我的魔法函数magic_function()

我可以这样做:

obj = MyObject()
obj = magic_function(obj)
obj.attr_one = 'new value'
obj.attr_two = 2

它会保存旧值,所以我可以这样做

print obj.old_attr_one
None
print obj.attr_one 'new value'

print obj.old_attr_two
1
print obj.attr_two
2

这样的东西......我想知道如何通过不触及实例类来实现这一点?

2 个答案:

答案 0 :(得分:4)

这是一个开始:

class MagicWrapper(object):
    def __init__(self, wrapped):
        self._wrapped = wrapped

    def __getattr__(self, attr):
        return getattr(self._wrapped, attr)

    def __setattr__(self, attr, val):
        if attr == '_wrapped':
            super(MagicWrapper, self).__setattr__('_wrapped', val)
        else:
            setattr(self._wrapped, 'old_' + attr, getattr(self._wrapped, attr))
            setattr(self._wrapped, attr, val)


class MyObject(object):
    def __init__(self):
        self.attr_one = None
        self.attr_two = 1

obj = MyObject()
obj = MagicWrapper(obj)
obj.attr_one = 'new value'
obj.attr_two = 2

print obj.old_attr_one
print obj.attr_one
print obj.old_attr_two
print obj.attr_two

当你试图包装奇怪的对象时,这不是防弹的(在Python中很少),但它应该适用于“普通”类。您可以编写更多代码以更接近完全克隆包装对象的行为,但可能无法完美地完成。这里要注意的主要事情是许多特殊方法不会被重定向到包装对象。

如果你想在不以某种方式包裹obj的情况下这样做,那就会变得混乱。这是一个选项:

def add_old_setattr_to_class(cls):
    def __setattr__(self, attr, val):
        super_setattr = super(self.__class__, self).__setattr__
        if attr.startswith('old_'):
            super_setattr(attr, val)
        else:
            super_setattr('old_' + attr, getattr(self, attr))
            super_setattr(attr, val)
    cls.__setattr__ = __setattr__


class MyObject(object):
    def __init__(self):
        self.attr_one = None
        self.attr_two = 1

obj = MyObject()
add_old_setattr_to_class(obj.__class__)
obj.attr_one = 'new value'
obj.attr_two = 2

print obj.old_attr_one
print obj.attr_one
print obj.old_attr_two
print obj.attr_two

请注意,如果您在外部提供的对象上使用它,则极具侵入性。它全局修改你正在应用魔法的对象的,而不仅仅是那个实例。这是因为像其他几个特殊方法一样,__setattr__没有在实例的属性字典中查找;查找会直接跳到类,因此无法在实例上覆盖__setattr__。如果我在野外遇到它,我会将这种代码描述为奇怪的黑客(当然,如果我自己写的那就是“漂亮的聪明”)。)。

对于已经使用__setattr____getattr__ / __getattribute__播放技巧的对象,此版本可能会或可能不会很好地发挥作用。如果您最终多次修改同一个类,我认为这仍然有效,但最终会有越来越多的包装__setattr__定义。你应该尽量避免这种情况;可能是通过在类上设置“秘密标志”并在修改add_old_setattr_to_class之前在cls中检查它。您应该使用比old_更不可能的前缀,因为您实际上是在尝试创建一个完整的独立命名空间。

答案 1 :(得分:0)

您可以在运行时使用自定义属性替换所有属性。你想要实现什么目标?也许迁移到完全不可变的类型会是更好的选择吗?