Python 如何识别属性';正在设置的属性?
我有一个具有多个属性的可变对象(我们称之为Python 如何识别属性';正在设置的属性?,python,properties,attributes,Python,Properties,Attributes,我有一个具有多个属性的可变对象(我们称之为mutable)。此对象本身是类Foo中的属性Foo是用户定义的,而mutable不是,因此不能更改 每当有人试图在mutable中设置属性时,我都需要进行计算。我面临的问题是,仅当设置了mutable本身而不是其属性时,才使用mutable属性。我已经设法解决了这个问题,但是使用了一些看起来更像是黑客的东西,而不是合理的Python代码 class Mutable(object): # Example class. def __init__
mutable
)。此对象本身是类Foo
中的属性Foo
是用户定义的,而mutable
不是,因此不能更改
每当有人试图在mutable
中设置属性时,我都需要进行计算。我面临的问题是,仅当设置了mutable
本身而不是其属性时,才使用mutable
属性。我已经设法解决了这个问题,但是使用了一些看起来更像是黑客的东西,而不是合理的Python代码
class Mutable(object): # Example class.
def __init__(self):
self.attr0 = 0
self.attr1 = 1
def __repr__(self):
return str(self.__dict__)[1:-1]
class Foo(object):
def __init__(self):
self._mutable = Mutable()
@property
def mutable(self):
print('mutable was read')
return self._mutable
@mutable.setter
def mutable(self, attr_value_pair):
attribute, value = attr_value_pair
setattr(self._mutable, attribute, value)
print('mutable.' + attribute, 'was set to', value)
bar = Foo()
print(bar.mutable) # 'mutable was read'
bar.mutable = ('attr0', 5) # 'mutable.attr0 was set to 5'
bar.mutable = ('attr1', 10) # 'mutable.attr1 was set to 10'
print(bar.mutable) # 'mutable was read'
# This is what I want to do but it only calls the getter.
bar.mutable.attr0 = 0 # 'mutable was read'
bar.mutable.attr1 = 1 # 'mutable was read'
当mutable
中的属性以一种更具python风格的方式设置时,是否有一种方法可以识别
编辑:澄清:
Foo
需要知道mutable
何时更改,因为Foo
具有依赖于mutable
的属性<代码>可变可以被继承。最简单的干净解决方案是将可变
子类化-但我认为这不是一个选项
最简单的“快速肮脏”解决方案是monkeypatchmutable
——但这实际上是一个最后的解决方案,从长远来看是在寻找麻烦
因此,如果您不能子类化mutable
也不能控制它的实例,并且不想对其进行monkeypatch,那么您就只能使用代理模式了
编辑:哦,是的,因为它是Foo
,需要得到关于self.\u mutable
更改的通知,所以您必须将其与观察者模式相结合(这里是一种非常受限的形式):
类可变(对象):#示例类。
定义初始化(自):
self.attr0=0
self.attr1=1
定义报告(自我):
返回str(self.\u dict\u)[1:-1]
类MutableProxy(对象):
定义初始化(自、可变、所有者):
自。_可变=可变
self.\u owner=所有者
@财产
def attr0(自身):
返回self.\u mutable.attr0
@属性0.setter
def attr0(自身,值):
self._mutable.attr0=值
self.\u owner.notify(“设置”、“属性0”、值)
@财产
def属性1(自身):
返回self.\u mutable.attr1
@属性1.setter
def属性1(自身,值):
self._mutable.attr1=值
自我通知(“属性1”,值)
定义报告(自我):
返回“”。格式(报告(自身可变))
类Foo(对象):
定义初始化(自):
self.mutable=mutable()
@财产
def可变(自):
#打印('mutable已读取')
返回自我
@可变设定器
def可变(自身、值):
self.\u mutable=MutableProxy(值,self)
def notify(自身、属性名称、值):
print('self.\u mutable.{}被设置为{}'。格式(attrname,value))
注意:我没有在MutableProxy中添加任何类型检查。\uuuu init\uuuu
,这取决于你真正的mutable
是什么,你可能需要确保你至少得到一些兼容的东西
NB2:我在ProxyMutable
上使用了显式属性,因为它使事情变得更清楚,但是您可能希望使用\uuuuu getattr\uuuuu
/\uuuuu setattr\uuuuu
钩子(至少对于不需要控制访问的可变属性)
NB3:我们现在在Foo
和MutableProxy
之间有一个循环引用。Python normalluy知道如何摆脱循环引用,但如果它碰巧对您的具体用例来说仍然是一个问题,那么您可能需要创建MutableProxy.\u owner
a
现在困扰我的问题是:为什么要公开mutable
?完全隐藏它并仅通过Foo
方法或属性提供对其属性的访问将使代码更简单(而且更容易推理,也不太可能产生意外的副作用)。Foo类无法检测何时发生可变更改
您需要扩展可变类并检测其中的更改,或者将其包装为从包装器读/写
类可变包装器(可变):
定义初始化(自):
self._mutable=mutable()
定义设置属性(自身、键、值):
打印('mutable.'+key'设置为',value)
超级()
def _uGetAttr _;(自身,项目):
打印('mutable已读取')
超级()
定义报告(自我):
return str(self.\u mutable.\uuuu dict\uuuuuu)[1:-1]
类Foo(对象):
定义初始化(自):
self._mutable=MutableWrapper()
@财产
def可变(自):
返回自我
@可变设定器
定义可变(自身、属性值对):
属性,值=属性值对
setattr(自可变、属性、值)
修订版
基本上我同意@Alessandro的观点,即您应该扩展Mutable
类,但是有几个显著的区别。一个是,假设它是从基类派生的,那么它也不需要包含一个单独的(未使用的)基类实例。在我之前的回答中,我忽略了这一点
更重要的是,它支持用户提供的回调函数,每当读取或写入它的一个属性时,就会调用这些回调函数。这允许将通知发送回contain类-Foo
中的方法,我认为在这种情况下确实需要处理它们的更改
注意:这并不一定意味着您可以删除Foo
属性mutable
。如果您想支持已实现的元组分配操作,则仍然需要它,该操作允许以下语句设置attr0
的attr0
属性:bar.mutable=('attr0',5)
。没有你的财产
class Mutable(object): # Example class.
def __init__(self):
self.attr0 = 0
self.attr1 = 1
def __repr__(self):
return str(self.__dict__)[1:-1]
class MutableProxy(object):
def __init__(self, mutable, owner):
self._mutable = mutable
self._owner = owner
@property
def attr0(self):
return self._mutable.attr0
@attr0.setter
def attr0(self, value):
self._mutable.attr0 = value
self._owner.notify("set", "attr0", value)
@property
def attr1(self):
return self._mutable.attr1
@attr1.setter
def attr1(self, value):
self._mutable.attr1 = value
self._owner.notify("attr1", value)
def __repr__(self):
return "<MutableProxy for {}>".format(repr(self._mutable))
class Foo(object):
def __init__(self):
self.mutable = Mutable()
@property
def mutable(self):
#print('mutable was read')
return self._mutable
@mutable.setter
def mutable(self, value):
self._mutable = MutableProxy(value, self)
def notify(self, attrname, value):
print('self._mutable.{} was set to {}'.format(attrname, value))
class Mutable(object): # Example class (unchangeable).
def __init__(self):
self.attr0 = 0
self.attr1 = 1
def __repr__(self):
return str(self.__dict__)[1:-1]
class MonitoredMutable(Mutable):
_get_callback = _set_callback = lambda *_: None # no-op placeholders
def __init__(self, get_callback, set_callback):
# use superclass to avoid infinite recursion when setting attributes
super_delegate = super(MonitoredMutable, self)
super_delegate.__init__()
super_delegate.__setattr__('_get_callback', get_callback)
super_delegate.__setattr__('_set_callback', set_callback)
def __setattr__(self, name, value):
super(MonitoredMutable, self).__setattr__(name, value)
self._set_callback(name, value) # write notification
def __getattr__(self, name):
self._get_callback(name) # read notification
return super(MonitoredMutable, self).__getattr__(name, value)
def __repr__(self): # optional
# override to only display the public attributes of the instance
public_attrs = {k:v for k,v in self.__dict__.items()
if not k.startswith('_')}
# assuming single inheritance (only one base class)
base_classname = self.__class__.__bases__[0].__name__
return base_classname + ': ' + (str(public_attrs)[1:-1] if public_attrs
else 'No pub attributes')
class Foo(object):
def __init__(self):
self._mutable = MonitoredMutable(self._get_callback, self._set_callback)
def _get_callback(self, name):
print('mutable.' + name + ' was read')
def _set_callback(self, name, value):
print('mutable.' + name, 'was set to', value)
@property
def mutable(self):
return self._mutable
@mutable.setter
def mutable(self, attr_value_pair):
attribute, value = attr_value_pair
setattr(self._mutable, attribute, value)
bar = Foo()
print(bar.mutable) # -> Mutable: 'attr0': 0, 'attr1': 1
bar.mutable = ('attr0', 5) # -> mutable.attr0 was set to 5
bar.mutable = ('attr1', 10) # -> mutable.attr1 was set to 10
print(bar.mutable) # -> Mutable: 'attr0': 5, 'attr1': 10
# These now work
bar.mutable.attr0 = 1 # -> mutable.attr0 was set to 1
bar.mutable.attr1 = 0 # -> mutable.attr1 was set to 0
print(bar.mutable) # -> Mutable: 'attr0': 1, 'attr1': 0
from types import FunctionType, MethodType
class Mutable(object): # Example class (unchangeable).
def __init__(self):
self.attr0 = 0
self.attr1 = 1
def __repr__(self):
return str(self.__dict__)[1:-1]
def monitor_attr_changes(obj, id, notify):
""" Change class of obj to one that supports attribute notifications. """
old_setattr = getattr(obj, '__setattr__')
old_classname = obj.__class__.__name__
class NewClass(obj.__class__):
def __setattr__(self, name, value):
old_setattr(name, value)
notify(id, name, value)
def __repr__(self): # Not required -- here only for demo purposes.
data_attrs = {name: value for name, value in self.__dict__.items()
if not isinstance(value, (FunctionType, MethodType))}
return old_classname + ': ' + str(data_attrs)[1:-1]
obj.__class__ = NewClass
return obj
class Foo(object):
def __init__(self, id):
print('creating instance {!r} of Mutable class'.format(id))
self.mutable = monitor_attr_changes(Mutable(), id, self._callback)
def _callback(self, id, name, value):
print('{} notification: {} has been set to {}'.format(id, name, value))
foo = Foo('foo')
bar = Foo('bar')
print(foo.mutable) # -> Mutable: 'attr0': 0, 'attr1': 1
foo.mutable.attr0 = 5 # -> foo notification: attr0 has been set to 5
bar.mutable.attr0 = 42 # -> bar notification: attr0 has been set to 42
foo.mutable.attr1 = 10 # -> foo notification: attr1 has been set to 10
print(foo.mutable) # -> Mutable: 'attr0': 5, 'attr1': 10
foo.mutable.attr0 = 1 # -> foo notification: attr0 has been set to 1
foo.mutable.attr1 = 0 # -> foo notification: attr1 has been set to 0
print(foo.mutable) # -> Mutable: 'attr0': 1, 'attr1': 0
print(foo.mutable.attr0) # -> 1
print(bar.mutable.attr0) # -> 42 x