Python @Property

Python @Property

原文链接
https://www.programiz.com/python-programming/property

在本教程中,您将学习Python @property装饰器。一种在面向对象程序设计中使用getter和setter的pythonic方式。

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
假设我们要扩展 摄氏温度上面定义的类。我们知道任何物体的温度都不能低于-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

如我们所见,以上方法引入了两个newget_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
追溯(最近一次通话):
<模块>中第30行的文件“ <字符串>”
set_temperature中的文件“ ”,第16行
ValueError:温度无法低于-273.15。
此更新成功实施了新限制。我们不再允许将温度设置为低于-273.15摄氏度。

注意:私有变量实际上在Python中不存在。有一些简单的准则可以遵循。语言本身没有任何限制。

human._temperature = -300
human.get_temperature()
-300
但是,上述更新的最大问题是,实现我们上一类的所有程序都必须将代码从obj.temperatureto修改,obj.get_temperature()并且所有表达式都喜欢obj.temperature = valto obj.set_temperature(val)。

在处理成千上万行代码时,这种重构可能会引起问题。

总而言之,我们的新更新不向后兼容。这是@property救援的地方。

物业类别
解决上述问题的一种Python方法是使用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。简而言之,property将一些代码(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
输出

设定值…
获得价值…
37
获得价值…
98.60000000000001
设定值…
追溯(最近一次通话):
<模块>中第31行的文件“ <字符串>”
set_temperature中的文件“ ”,第18行
ValueError:温度不能低于-273
如我们所见,任何检索到的值的代码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 Decorators的程序员可以认识到上述构造可以实现为装饰器。

我们甚至无法定义名称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)
输出

设定值…
获得价值…
37
获得价值…
98.60000000000001
设定值…
追溯(最近一次通话):
<模块>中第29行的文件“ <字符串>”
__init__中第4行的文件“ ”
温度中的文件“ ”,第18行
ValueError:温度不能低于-273
上面的实现是简单而有效的。这是推荐的使用方式property。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值