就地修改属性时,如何使属性装饰器被调用?

时间:2019-05-08 18:03:47

标签: python dictionary properties decorator python-decorators

我想知道如何在修改对象而不是为其分配对象时使@property装饰器调用构造函数。

class Celsius:
    def __init__(self, temperature = [0]):
        self._temperature = temperature
    @property
    def temperature(self):
        print("Getting value")
        return self._temperature
    @temperature.setter
    def temperature(self, value):
        print('setting temperature')
        self._temperature = value
a=Celsius()
a.temperature
Getting value
Out[10]: [0]
a.temperature = 7
setting temperature
a.temperature = [7,8]
setting temperature
a.temperature[0] = 0
Getting value
a.temperature
Getting value
Out[14]: [0, 8]

如您所见,如果分配温度,则属性会被调用,而如果列表被修改,它不会调用构造函数,而是会调用“显示器”

我知道@property可能无法完成此操作,但这是我找到的最接近的主意。

实际上,它如何改变温度和_temperature的值?

2 个答案:

答案 0 :(得分:0)

设置值时看到“获取值”输出的原因是因为您正在修改_temperature变量。

要修改变量,必须首先对其进行检索。覆盖变量时,不需要首先检索它,而只需对其进行设置即可。

之所以看不到“设置温度”输出,是因为您实际上是在修改_temperature的基础对象(一个列表),所以不使用属性设置器。

答案 1 :(得分:0)

顺序:

>>> a=Celsius()
>>> a.temperature = [7,8]
setting temperature
>>> a.temperature[0] = 0
getting value

特别有趣,因为您授予了_temperature列表的访问权限,并且邪恶的用户可能破坏您的Celsius实例。一个人想要这样的东西:

>>> a=Celsius()
>>> a.temperature = [7,8]
setting temperature (I CHECK THAT THOSE VALUES ARE CONSISTENT)
>>> a.temperature[0] = 0
setting ONE temperature (I CHECK THAT THIS VALUE IS CONSISTENT)

基本上,您无法控制对象,因为您无法检查邪恶用户可以做什么。这似乎是一个危险的设计。这是一个建议:永远不要与外部世界共享一个可变的对象,始终提供一个不变的对象或副本。(这可以确保遵守Law of Demeter)。替换:

@property
def temperature(self):
    print("getting value")
    return self._temperature

通过:

@property
def temperature(self):
    print("getting value")
    return list(self._temperature)

并尝试您的测试用例:

>>> a=Celsius()
>>> a.temperature = [7,8]
setting temperature (I CHECK THAT THOSE VALUES ARE CONSISTENT)
>>> a.temperature[0] = 0
getting value
>>> a.temperature
getting value
[7, 8] # NO MODIFICATION HERE

现在,您的Celsius对象受到了保护,不能进行野蛮的修改:如果要更改_temperature列表,则必须提供一个完整的列表,设置者应检查该列表。

如果要更改单个值,仍然可以,但是可以调用设置器(并可能进行检查):

>>> a=Celsius()
>>> a.temperature = [7,8]
setting temperature (I CHECK THAT THOSE VALUES ARE CONSISTENT)
>>> new_temp = [0] + a.temperature[1:]
getting value
>>> a.temperature = new_temp
setting temperature (I CHECK THAT THOSE VALUES ARE CONSISTENT)
>>> a.temperature
getting value
[0, 8]

如果您绝对希望用户按索引设置项目,则必须手动编写函数:

class Celsius:
    ...

    def set_temperature_item(self, i, v):
        print ("setting ONE temperature (I CHECK THAT THIS VALUE IS CONSISTENT)")
        self._temperature[i] = v

>>> a=Celsius()
>>> a.temperature = [7,8]
setting temperature (I CHECK THAT THOSE VALUES ARE CONSISTENT)
>>> a.set_temperature_item(0, 0)
setting ONE temperature (I CHECK THAT THIS VALUE IS CONSISTENT)
>>> a.temperature
getting value
[0, 8]

属性

我认为您不能使用property来完成此操作,因为在您写时:

a.temperature[0] = 0

等效于:

a.__getattribute__("temperature").__setitem__(0, 0)

您无法确定__getattribute__调用下一步将发生什么:仅获取值或使用它来设置项目。