【问题标题】:Is there an easy way to notify other fields whenever a @property field was modified是否有一种简单的方法可以在修改 @property 字段时通知其他字段
【发布时间】:2019-04-26 17:42:06
【问题描述】:

我正在尝试定义一个集成了多种色彩空间表达的类。

即,

#'hsv', 'rgb'...such fields are different color space expression of one thing.

my_color = MyColor('#ffffff')
my_color.rgb   #(1.0, 1.0, 1.0)
my_color.rgb24 #(255, 255, 255)
my_color.hsv   #(0.0. 0.0, 1.0)

而且出于某种目的,我希望这个类可以支持一些基本的数学运算,

my_color.hsv[2] -= 1.0

当然,当任何其他“绑定”字段被修改时,所有字段都应该同步它们的值。

#after operation above.
my_color.rgb   #(0.0, 0.0, 0.0)
my_color.rgb24 #(0.0, 0.0, 0.0)
my_color.hsv   #(0.0, 0.0, 0.0)

最好的方法是什么。

我已经尝试过@property 和@setter 注释。

@property
def hsv(self):
    return self._hsv

@hsv.setter
def hsv(self, val):
    self._hsv = val
    _sync_change()  #update other fields by the new hsv value

我希望@setter 可以帮助我根据新的“hsv”值更新其他字段。当我对 my_color.hsv 进行赋值操作时,它确实有效。

my_color.hsv = (0.5, 0.5, 0.5)
print(my_color.rgb, my_color.rgb24) #shows correct value.

但是,当我进行修改时,不是在元组上而是在元组的一个元素上,事情就出错了。

my_color.hsv[2] -= 1.0
print(my_color.rgb, my_color.rgb24) #nothing changed.
print(my_color.hsv)  #nothing changed.

我想知道当我弄错时会发生什么,我可以用@setter 方式做什么,或者有没有更好更自然的方式。

【问题讨论】:

  • my_color.hsv[2] -= 1.0 不会在没有真正讨厌的、不值得的、错误滋生的自定义序列类型的情况下进行同步。

标签: python class class-design color-conversion


【解决方案1】:

您的属性返回一个列表对象,该列表对象本质上是独立的。就属性而言,您只能读取该列表对象。

你的扩充赋值语句:

my_color.hsv[2] -= 1.0

否则不会设置该属性。它在列表中设置一个索引。以下大致等价:

_hsv_list = my_color.hsv
_hsv_list[2] = _hsv_list[2].__isub__(1.0)  # __isub__ is called to handle -=

那里没有my_color.hsv = _hsv_list,所以你的setter永远不会被调用。

所以诀窍是返回一个自定义对象,让您拦截__setitem__ 和其他访问:

from collections.abc import Sequence

class HSVValues(Sequence):
    _parent: MyColor

    def __init__(self, parent):
        self._parent = parent

    def __len__(self):
        return len(self._parent._hsv)

    def __getitem__(self, item):
        return self._parent._hsv[item]

    def __setitem__(self, item, value):
        self._parent._hsv[item] = value
        self._parent._sync_change()

并从您的财产归还:

@property
def hsv(self):
    return HSVValues(self)

所以现在my_color.hsv 返回的不是_hsv 值,而是上述类的一个实例。

使用该对象替换原始列表,_hsv_list[2] = _hsv_list[2].__isub__(1.0) 将调用__setitem__,然后首先更新self._parent._hsv[2],然后调用父类上的_sync_change() 方法以计算所有其他值。

您可能希望_sync_changes 接受有关更改内容的更多信息;如果它知道_hsv[2] 发生了变化,那可能会提高同步操作的效率。

【讨论】:

  • “你的属性返回一个列表对象”——好吧,可能,无论如何。他们声称它是一个元组,并且他们分配了一个元组,但如果它是一个元组,my_color.hsv[2] -= 1.0 会引发一个TypeError,所以有些事情是不对的。可能在_sync_change 中发生了一些事情,或者可能是提问者没有告诉我们的其他事情。
  • @user2357112:在我看来,这一切都像是他们抽象的太多了,因为_sync_change() 也没有引用调用实例。
  • 是的,该代码没有意义。 (此外,您的 __getitem__ 正在返回整个 _hsv 对象而不仅仅是一个元素,并且您的 self._parent._sync_change 不会让父级知道要从哪个表示同步更改 - 代码中也存在一个缺陷,如发布在问题。)
  • @user2357112:谢谢,我纠正了我的__getitem__ 错误。是的,我没有提到_sync_change 以及传递更多细节的好处。
  • 抱歉打错了,我只是凭记忆写了那些代码。它应该是 self._sync_change()_hsv 是一个列表。实际上,我为每个实例维护了一个 _BASE_RGB_VAL 字段。我的期望是,每当修改其他字段(无论是列表本身还是元素)时,实例都会感知到这种修改,并将任何 ('_hsv', '_lab', ...) 表达式的新值转换为 RGB 表达式,然后self._sync_change()首先将RGB值更新为维护的_BASE_RGB_VAL,然后根据它更新所有其他字段。
猜你喜欢
  • 2019-08-18
  • 2019-09-30
  • 2012-10-28
  • 1970-01-01
  • 2023-03-21
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多