我想实现以任何对象作为参数的函数,并跟踪特定属性的值的变化。比在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
这样的东西......我想知道如何通过不触及实例类来实现这一点?
答案 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)
您可以在运行时使用自定义属性替换所有属性。你想要实现什么目标?也许迁移到完全不可变的类型会是更好的选择吗?