企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# Python `@property`装饰器 > 原文: [https://www.programiz.com/python-programming/property](https://www.programiz.com/python-programming/property) #### 在本教程中,您将学习 Python `@property`装饰器。 在面向对象程序设计中使用获取器和设置器的 pythonic 方式。 Python 编程为我们提供了一个内置的`@property`装饰器,使面向对象编程中的获取器和设置器的使用变得更加容易。 在详细介绍`@property`装饰器是什么之前,让我们首先了解一下为什么首先需要使用它。 * * * ## 没有获取器和设置器的类 让我们假设我们决定制作一个[类](/python-programming/class),该类存储以摄氏度为单位的温度。 它还将实现一种将温度转换为华氏度的方法。 一种方法如下: ```py class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 ``` 我们可以根据需要创建对象,并根据需要操纵`temperature`属性: ```py # Basic method of setting and getting attributes in Python class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # Create a new object human = Celsius() # Set the temperature human.temperature = 37 # Get the temperature attribute print(human.temperature) # Get the to_fahrenheit method print(human.to_fahrenheit()) ``` **输出** ```py 37 98.60000000000001 ``` 转换为华氏温度时,多余的小数位是由于浮点运算错误。 要了解更多信息,请访问 [Python 浮点算术错误](/python-programming/numbers#dec)。 每当我们如上所述分配或检索任何对象属性(如`temperature`)时,Python 都会在对象的内置`__dict__`字典属性中进行搜索。 ```py >>> human.__dict__ {'temperature': 37} ``` 因此,`man.temperature`在内部变为`man.__dict__['temperature']`。 * * * ## 使用获取器和设置器 假设我们要扩展上面定义的`Celsius`类的可用性。 我们知道任何物体的温度都不能低于 -273.15 摄氏度(热力学中的绝对零) 让我们更新代码以实现此值约束。 上述限制的一个明显解决方案是隐藏属性`temperature`(将其设为私有),并定义新的获取器和设置器方法来对其进行操作。 可以按以下步骤完成: ```py # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value ``` 如我们所见,以上方法引入了两个新的`get_temperature()`和`set_temperature()`方法。 此外,`temperature`被替换为`_temperature`。 开头的下划线`_`用于表示 Python 中的私有变量。 * * * 现在,让我们使用以下实现: ```py # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value # Create a new object, set_temperature() internally called by __init__ human = Celsius(37) # Get the temperature attribute via a getter print(human.get_temperature()) # Get the to_fahrenheit method, get_temperature() called by the method itself print(human.to_fahrenheit()) # new constraint implementation human.set_temperature(-300) # Get the to_fahreheit method print(human.to_fahrenheit()) ``` **输出**: ```py 37 98.60000000000001 Traceback (most recent call last): File "<string>", line 30, in <module> File "<string>", line 16, in set_temperature ValueError: Temperature below -273.15 is not possible. ``` 此更新成功实现了新限制。 我们不再被允许将温度设置为低于-273.15 摄氏度。 **注意**:私有变量实际上在 Python 中不存在。 只需遵循一些规范。 语言本身没有任何限制。 ```py >>> human._temperature = -300 >>> human.get_temperature() -300 ``` 但是,上述更新的更大问题是,实现我们上一类的所有程序都必须将其代码从`obj.temperature`修改为`obj.get_temperature()`,并将所有表达式从`obj.temperature = val`修改为`obj.set_temperature(val)`。 在处理成千上万行代码时,这种重构可能会引起问题。 总而言之,我们的新更新不向后兼容。 这是`@property`抢救的地方。 * * * ## 属性类 解决上述问题的一种 Python 方法是使用`property`类。 这是我们如何更新代码的方法: ```py # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value...") return self._temperature # setter def set_temperature(self, value): print("Setting value...") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature) ``` 我们在`get_temperature()`和`set_temperature()`内部添加了`print()`函数,以清楚地观察它们正在执行。 代码的最后一行创建一个属性对象`temperature`。 简而言之,属性将一些代码(`get_temperature`和`set_temperature`)附加到成员属性访问(`temperature`)。 让我们使用以下更新代码: ```py # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value...") return self._temperature # setter def set_temperature(self, value): print("Setting value...") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300 ``` **输出**: ```py Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 31, in <module> File "<string>", line 18, in set_temperature ValueError: Temperature below -273 is not possible ``` 如我们所见,任何检索`temperature`值的代码都会自动调用`get_temperature()`,而不是字典(__dict__)查找。 类似地,任何为`temperature`赋值的代码都会自动调用`set_temperature()`。 我们甚至可以在上面看到即使创建对象时也调用了`set_temperature()`。 ```py >>> human = Celsius(37) Setting value... ``` **你能猜出为什么吗?** 原因是在创建对象时,将调用`__init__()`方法。 该方法具有行`self.temperature = temperature`。 该表达式自动调用`set_temperature()`。 同样,任何访问`c.temperature`都会自动调用`get_temperature()`。 这就是财产的作用。 这里还有一些例子。 ```py >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001 ``` 通过使用`property`,我们可以看到在实现值约束时不需要修改。 因此,我们的实现是向后兼容的。 **注意**:实际温度值存储在私有`_temperature`变量中。`temperature`属性是为该私有变量提供接口的属性对象。 * * * ## `@property`装饰器 在 Python 中,`property()`是一个内置函数,可创建并返回`property`对象。 该函数的语法为: ```py property(fget=None, fset=None, fdel=None, doc=None) ``` 哪里, * `fget`是获取属性值的函数 * `fset`是设置属性值的函数 * `fdel`是删除属性的函数 * `doc`是一个字符串(如注释) 从实现中可以看出,这些函数参数是可选的。 因此,可以简单地如下创建属性对象。 ```py >>> property() <property object at 0x0000000003239B38> ``` 属性对象具有`getter()`,`setter()`和`deleter()`三种方法,以便在以后指定`fget`,`fset`和`fdel`。 这意味着,该行: ```py temperature = property(get_temperature,set_temperature) ``` 可以细分为: ```py # make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature) ``` 这两段代码是等效的。 熟悉 [Python 装饰器](/python-programming/decorator)的程序员可以认识到上述构造可以实现为装饰器。 我们甚至无法定义名称`get_temperature`和`set_temperature`,因为它们是不必要的,并污染了类名称空间。 为此,我们在定义获取器和设置器函数的同时重用`temperature`名称。 让我们看一下如何将其实现为装饰器: ```py # Using @property decorator 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.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300) ``` **输出**: ```py Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 29, in <module> File "<string>", line 4, in __init__ File "<string>", line 18, in temperature ValueError: Temperature below -273 is not possible ``` 上面的实现是简单而有效的。 建议使用`property`的方式。