亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> Python

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_temperatureset_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)

哪里,

从实现中可以看出,这些函数参数是可选的。因此,可以简单地创建一个属性对象,如下所示。

>>> property()
<property object at 0x0000000003239B38>

一个属性对象有三个方法,getter() , setter() , 和 deleter() 指定 fget , fsetfdel 稍后。这意味着,行:

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_temperatureset_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

  1. Python 数据类型
  2. Python 运算符
  3. Python pass 语句
  4. Python 函数参数
  5. Python字典
  6. Python 继承
  7. Python 运算符重载
  8. Python 迭代器
  9. Python 闭包
  10. Python @property 装饰器
  11. Python 日期时间
  12. Python 睡眠()