Python @property 装饰器
Python @property 装饰器
在本教程中,您将了解 Python @property 装饰器;在面向对象编程中使用 getter 和 setter 的 Python 方式。
Python编程为我们提供了内置的@property 装饰器,它使 getter 和 setter 在面向对象编程中的使用变得更加容易。
在详细了解 @property 之前 装饰器是,让我们首先建立一个直觉,了解为什么首先需要它。
没有 Getter 和 Setter 的类
让我们假设我们决定创建一个以摄氏度为单位存储温度的类。它还将实现一种将温度转换为华氏度的方法。一种方法如下:
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
我们可以从这个类中创建对象并操作 temperature 我们希望的属性:
# 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()) 输出
37 98.60000000000001
转换成华氏时的额外小数位是由于浮点算术错误。要了解更多信息,请访问 Python 浮点算术错误。
每当我们分配或检索任何对象属性时,例如 temperature 如上图,Python在对象内置的__dict__中搜索 字典属性。
>>> human.__dict__
{'temperature': 37}
因此,man.temperature 内部变为 man.__dict__['temperature'] .
使用 Getter 和 Setter
假设我们想扩展 Celsius 的可用性 上面定义的类。我们知道任何物体的温度都不能低于-273.15摄氏度(热力学绝对零)
让我们更新我们的代码来实现这个值约束。
对上述限制的一个明显解决方案是隐藏属性 temperature (使其私有)并定义新的 getter 和 setter 方法来操作它。可以这样做:
# 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 中的私有变量。
现在,让我们使用这个实现:
# 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())
输出
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 中。只需遵守一些规范即可。语言本身没有任何限制。
>>> human._temperature = -300
>>> human.get_temperature()
-300
然而,上述更新更大的问题是所有实现我们之前类的程序都必须从 obj.temperature 修改它们的代码 到 obj.get_temperature() 以及像 obj.temperature = val 这样的所有表达式 到 obj.set_temperature(val) .
这种重构在处理数十万行代码时可能会出现问题。
总而言之,我们的新更新不向后兼容。这是 @property 来救援。
属性类
处理上述问题的pythonic方法是使用property 班级。以下是我们如何更新代码:
# 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)
我们添加了一个 print() get_temperature() 内的函数 和 set_temperature() 清楚地观察他们正在被处决。
代码的最后一行创建了一个属性对象 temperature .简单地说,属性附加了一些代码(get_temperature 和 set_temperature ) 到成员属性访问 (temperature )。
让我们使用这个更新代码:
# 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 输出
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() 即使我们创建了一个对象也会被调用。
>>> human = Celsius(37)
Setting value... 你能猜出原因吗?
原因是在创建对象时,__init__() 方法被调用。这个方法有 self.temperature = temperature 行 .此表达式自动调用 set_temperature() .
同样,像 c.temperature 这样的任何访问 自动调用get_temperature() .这就是属性的作用。这里还有几个例子。
>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value
>>> c.to_fahrenheit()
Getting value
98.60000000000001
通过使用 property ,我们可以看到值约束的实现不需要修改。因此,我们的实现是向后兼容的。
注意 :实际温度值存储在私有_temperature中 多变的。 temperature 属性是一个属性对象,它为这个私有变量提供了一个接口。
@property 装饰器
在 Python 中,property() 是一个内置函数,它创建并返回 property 目的。这个函数的语法是:
property(fget=None, fset=None, fdel=None, doc=None) 哪里,
fget是获取属性值的函数fset是设置属性值的函数fdel是删除属性的函数doc是一个字符串(如评论)
从实现中可以看出,这些函数参数是可选的。因此,可以简单地创建一个属性对象,如下所示。
>>> property()
<property object at 0x0000000003239B38>
一个属性对象有三个方法,getter() , setter() , 和 deleter() 指定 fget , fset 和 fdel 稍后。这意味着,行:
temperature = property(get_temperature,set_temperature) 可以分解为:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature) 这两段代码是等价的。
熟悉 Python 装饰器的程序员可以认识到,上述构造可以作为装饰器来实现。
我们甚至不能定义名称 get_temperature 和 set_temperature 因为它们是不必要的并且会污染类命名空间。
为此,我们重用 temperature 在定义我们的 getter 和 setter 函数时命名。让我们看看如何将其实现为装饰器:
# 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) 输出
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "", line 29, in File " ", line 4, in __init__ File " ", line 18, in temperature ValueError: Temperature below -273 is not possible
上述实现简单高效。推荐使用 property .
Python