Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何识别属性';正在设置的属性?_Python_Properties_Attributes - Fatal编程技术网

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
的属性<代码>可变可以被继承。

最简单的干净解决方案是将
可变
子类化-但我认为这不是一个选项

最简单的“快速肮脏”解决方案是monkeypatch
mutable
——但这实际上是一个最后的解决方案,从长远来看是在寻找麻烦

因此,如果您不能子类化
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