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