【问题标题】:How to make property decorator to be called when in-place modifying an attribute?如何在就地修改属性时调用属性装饰器?
【发布时间】:2023-03-20 11:11:01
【问题描述】:

我想知道如何让@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 可能无法做到这一点,但这是我找到的最接近的想法。

实际上,它是如何就地改变温度和_温度的值的?

【问题讨论】:

    标签: python dictionary properties decorator python-decorators


    【解决方案1】:

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

    要修改变量,必须首先检索它。当你覆盖一个变量时,它不需要先被检索,只需设置即可。

    您没有看到“设置温度”输出的原因是因为您实际上正在修改 _temperature 的底层对象,一个列表,所以您的属性设置器没有被使用。

    【讨论】:

      【解决方案2】:

      顺序:

      >>> 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 列表,您必须提供一个完整的列表,setter 应该检查该列表。

      如果您想更改单个值,这仍然是可能的,但是会调用 setter(以及可能的检查):

      >>> 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__ 调用接下来会发生什么:只需获取值或使用它来设置项目。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 2012-12-02
        • 2019-07-29
        • 2018-11-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多