我正在尝试定义一个与多种颜色空间表达方式集成的类。
即
#'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方式进行处理,或者有一种更好,更自然的方式进行处理?
答案 0 :(得分:0)
您的属性返回一个列表对象,并且该列表对象本质上是独立的。就属性而言,您只需读取该列表对象。
您的增值作业声明:
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
,所以您的二传手永远不会被呼叫。
因此,诀窍是返回一个自定义对象,该对象可让您拦截__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]
进行了更改,则可以使同步操作更有效。