当 属性 用作装饰器时, setter 不会引发异常

When property is used as decorator, the setter does not raise the exception

我正在学习 Python,@属性 发生了一件奇怪的事情,我不明白...

我找到了一个关于 属性 如何工作的示例 here,我发现它非常清楚。 以下代码工作正常:

class Celsius:

    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_farenheit(self):
        return (self.temperature * 1.8) + 32

    def get_temperature(self):
        print("Getting value")  # just to see on screen when we call it
        return self._temperature

    def set_temperature(self, value):
        print("Setting value")  # just to see on screen when we call it
        if value < -273:
            raise ValueError("Temperature cannot be less than -273 °C !")
        self._temperature = value

    temperature = property(get_temperature, set_temperature)

当我们尝试将温度设置为绝对零以下时,我们看到调用了 setter 并引发了 ValueError:

T = Celsius(-400)
>>> Setting value
>>> Traceback (most recent call last):
>>> ...
>>> ValueError: Temperature cannot be less than -273 °C !

然而,当我们将属性定义为装饰器时(即@属性),如下面的代码:

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

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

    @temperature.setter
    def temperature(self, value):
        print("Setting value")
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

我们做同样的事情,没有调用 setter 也没有引发异常:

T = Celsius(-400)
print(T.temperature)
>>> Getting value
>>> -400

看起来 @temperature.setter 从未被调用过,即使我们改变了 T 的值:

T = Celsius(20)
print(T.temperature)
>>> Getting value
>>> -400

我不明白...有人可以给我解释一下吗?

在您的 class 启动器中,您将输入 temperature 设置为实例属性 _temperature,但是您有 属性 getter 和 setter对于属性 temperature.

在当前形式中,如果您要设置属性 temperature,您将获得所需的异常:

In [505]: c = Celsius(-400)                                                                                                                                                                                 

In [506]: c.temperature                                                                                                                                                                                     
Getting value
Out[506]: -400

In [507]: c.temperature = -500                                                                                                                                                                              
Setting value
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-507-2b9ed72eeb52> in <module>
----> 1 c.temperature = -500

<ipython-input-504-e44aad21660f> in temperature(self, value)
     15         print("Setting value")
     16         if value < -273:
---> 17             raise ValueError("Temperature below -273 is not possible")
     18         print("Setting value")
     19         self._temperature = value

ValueError: Temperature below -273 is not possible

给你。

现在,如果您更改 __init__ 以将输入 temperature 设置为 temperature 属性,则在初始化 class:

In [508]: class Celsius: 
     ...:     def __init__(self, temperature = 0): 
     ...:         self.temperature = temperature 
     ...:  
     ...:     def to_fahrenheit(self): 
     ...:         return (self.temperature * 1.8) + 32 
     ...:  
     ...:     @property 
     ...:     def temperature(self): 
     ...:         print("Getting value") 
     ...:         return self.temperature 
     ...:  
     ...:     @temperature.setter 
     ...:     def temperature(self, value): 
     ...:         print("Setting value") 
     ...:         if value < -273: 
     ...:             raise ValueError("Temperature below -273 is not possible") 
     ...:         print("Setting value") 
     ...:         self.temperature = value 
     ...:                                                                                                                                                                                                   

In [509]: c = Celsius(-400)                                                                                                                                                                                 
Setting value
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-509-1fc014754084> in <module>
----> 1 c = Celsius(-400)

<ipython-input-508-af8466917803> in __init__(self, temperature)
      1 class Celsius:
      2     def __init__(self, temperature = 0):
----> 3         self.temperature = temperature
      4 
      5     def to_fahrenheit(self):

<ipython-input-508-af8466917803> in temperature(self, value)
     15         print("Setting value")
     16         if value < -273:
---> 17             raise ValueError("Temperature below -273 is not possible")
     18         print("Setting value")
     19         self.temperature = value

ValueError: Temperature below -273 is not possible

为什么你的第一个代码有效?

在您的第一个代码中,您已将属性 temperature 设置为 __init__ 中的输入 temperature 值。

由于使用了 property 可调用(它是一个描述符),python 看到你有一个描述符 temperature 并且它是一个数据描述符,因为你有一个 setter 还有。因此它尝试调用 setter 方法来设置值。