每次对属性进行更改时,我都会使用属性来执行某些代码,如下所示:
class SomeClass(object):
def __init__(self,attr):
self._attr = attr
@property
def attr(self):
return self._attr
@attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
这很有效:
>>> a = SomeClass(5)
>>> a.attr = 10
Do some code here every time attr changes
但如果我在attr
中存储了一个可变对象,则可以直接修改attr
,绕过setter和我的更改检测代码:
class Container(object):
def __init__(self,data):
self.data = data
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
>>>
假设attr
仅用于存储Container
类型的对象。每当SomeClass
中存储的Container
对象被修改时,是否有一种优雅的方法可以修改SomeClass
和/或_on_change
以使Container
执行attr
?换句话说,我希望我的输出为:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
答案 0 :(得分:4)
以下是我认为具有您正在寻找的行为的SomeClass
和Container
版本。这里的想法是对Container
的修改将调用与其关联的_on_change()
实例的SomeClass
函数:
class Container(object):
def __init__(self, data):
self.data = data
def __setattr__(self, name, value):
if not hasattr(self, name) or getattr(self, name) != value:
self.on_change()
super(Container, self).__setattr__(name, value)
def on_change(self):
pass
class SomeClass(object):
def __init__(self, attr):
self._attr = attr
self._attr.on_change = self._on_change
@property
def attr(self):
return self._attr
@attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
示例:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
>>> b.attr.data = 10 # on_change() not called if the value isn't changing
>>> b.attr.data2 = 'foo' # new properties being add result in an on_change() call
Do some code here every time attr changes
请注意,对SomeClass
的唯一更改是__init__()
中的第二行,我只是包含了完整性和易于测试的完整代码。
答案 1 :(得分:0)
如果您想跟踪更改并且不想在不同的类中使用on_change()
方法,那么您可以使用functools.partial
开头here显示的方式。
这样您就可以包装数据并完全隐藏它。只有通过在该对象内融合的某些方法才能获得/更改。
NB :python没有私有属性,按照惯例,我们都是成年人并且违反了规则。在您的情况下,您的api用户不应直接更改容器上的数据(创建后)。
NB :对于那些可能对其他方式感兴趣的人......
答案 2 :(得分:0)
这是另一种解决方案。某种代理类。您不需要修改任何类来监视它们中的属性更改,只需使用ovverriden ChangeTrigger
函数在_on_change
派生类中包装对象:
class ChangeTrigger(object):
def __getattr__(self, name):
obj = getattr(self.instance, name)
# KEY idea for catching contained class attributes changes:
# recursively create ChangeTrigger derived class and wrap
# object in it if getting attribute is class instance/object
if hasattr(obj, '__dict__'):
return self.__class__(obj)
else:
return obj
def __setattr__(self, name, value):
if getattr(self.instance, name) != value:
self._on_change(name, value)
setattr(self.instance, name, value)
def __init__(self, obj):
object.__setattr__(self, 'instance', obj)
def _on_change(self, name, value):
raise NotImplementedError('Subclasses must implement this method')
示例:
class MyTrigger(ChangeTrigger):
def _on_change(self, name, value):
print "New value for attr %s: %s" % (name, value)
class Container(object):
def __init__(self, data):
self.data = data
class SomeClass(object):
attr_class = 100
def __init__(self, attr):
self.attr = attr
self.attr_instance = 5
>>> a = SomeClass(5)
>>> a = MyTrigger(a)
>>>
>>> a.attr = 10
New value for attr attr: 10
>>>
>>> b = SomeClass(Container(5))
>>> b = MyTrigger(b)
>>>
>>> b.attr.data = 10
New value for attr data: 10
>>> b.attr_class = 100 # old value = new value
>>> b.attr_instance = 100
New value for attr attr_instance: 100
>>> b.attr.data = 10 # old value = new value
>>> b.attr.data = 100
New value for attr data: 100